diff --git a/.github/claude/readme.md b/.github/claude/readme.md
new file mode 100644
index 0000000..f471761
--- /dev/null
+++ b/.github/claude/readme.md
@@ -0,0 +1,238 @@
+# SocialCalc
+
+A collaborative spreadsheet application built with JavaScript that provides real-time editing capabilities and comprehensive spreadsheet functionality. SocialCalc offers a web-based alternative to traditional desktop spreadsheet applications with built-in collaboration features and extensive formula support.
+
+## ๐ Features
+
+- **Interactive Spreadsheet Interface** - Full-featured spreadsheet with cells, formulas, and formatting
+- **Real-time Collaboration** - Multi-user editing capabilities with live updates
+- **Comprehensive Formula Engine** - Support for mathematical, logical, and text functions
+- **Rich Text Formatting** - Cell styling, alignment, borders, and number formatting
+- **Data Visualization** - Built-in charting and graphing capabilities
+- **Import/Export Support** - Compatible with standard spreadsheet formats
+- **Undo/Redo System** - Complete command history with reversible operations
+- **Server-side Processing** - Perl backend for data persistence and collaboration
+- **Cross-platform Compatibility** - Works in modern web browsers
+- **Extensible Architecture** - Modular design for custom functionality
+
+## ๐ ๏ธ Tech Stack
+
+### Frontend
+- **JavaScript (ES5)** - Core application logic and user interface
+- **HTML4** - Markup structure and layout
+- **CSS** - Styling and visual presentation
+- **DOM Manipulation** - Direct browser API usage for UI updates
+
+### Backend/Services
+- **Perl** - Server-side scripting and utilities
+- **SocialCalc Server Components** - Custom Perl modules for spreadsheet operations
+
+### Development Tools
+- **Git** - Version control system
+- **npm** - Package management and scripting
+- **CommonJS** - Module system for JavaScript components
+
+### Architecture
+- **Client-Server Model** - Web-based frontend with Perl backend
+- **Component-based Design** - Modular JavaScript architecture
+- **Event-driven Programming** - Interactive user interface with real-time updates
+
+## ๐ Project Structure
+
+```
+socialcalc/
+โโโ socialcalc-3.js # Core SocialCalc engine
+โโโ socialcalctableeditor.js # Table editing functionality
+โโโ socialcalcspreadsheetcontrol.js # Main spreadsheet controller
+โโโ socialcalcviewer.js # Read-only spreadsheet viewer
+โโโ socialcalcpopup.js # Dialog and popup management
+โโโ socialcalcconstants.js # Application constants and configuration
+โโโ formatnumber2.js # Number formatting utilities
+โโโ formula1.js # Formula parsing and execution engine
+โโโ socialcalc.css # Main stylesheet
+โโโ images/ # UI icons and graphics
+โโโ *.html # Demo pages and test applications
+โโโ *.pl # Perl server-side scripts
+โโโ SocialCalcServersideUtilities.pm # Perl utility modules
+```
+
+## ๐ง Installation & Setup
+
+### Prerequisites
+- Modern web browser (Chrome, Firefox, Safari, Edge)
+- Web server (Apache, Nginx, or development server)
+- Perl 5.x (for server-side functionality)
+- npm (Node.js package manager)
+
+### Installation Steps
+```bash
+# Clone the repository
+git clone https://github.com/anisharma07/socialcalc.git
+
+# Navigate to project directory
+cd socialcalc
+
+# Install dependencies
+npm install
+
+# Set up web server to serve static files
+# Point document root to the socialcalc directory
+
+# For Perl server components (optional)
+perl -c SocialCalcServersideUtilities.pm
+```
+
+## ๐ฏ Usage
+
+### Development
+```bash
+# Serve files using a simple HTTP server
+# Python 3
+python -m http.server 8000
+
+# Python 2
+python -m SimpleHTTPServer 8000
+
+# Node.js (if http-server is installed)
+npx http-server
+
+# Access demos at:
+# http://localhost:8000/socialcalc2demo-0-8-1.html
+# http://localhost:8000/socialcalc2demo10.html
+# http://localhost:8000/ssctrltest1.html
+```
+
+### Production
+```bash
+# Deploy static files to web server
+cp -r socialcalc/ /var/www/html/
+
+# Configure server to serve .pl files (for Perl backend)
+# Ensure CGI is enabled for Perl scripts
+# Set appropriate permissions for .pl files
+chmod +x *.pl
+```
+
+### Integration
+```javascript
+// Basic SocialCalc integration
+// Include required JavaScript files in your HTML:
+
+
+
+
+
+
+
+
+// Initialize spreadsheet
+var spreadsheet = new SocialCalc.SpreadsheetControl();
+spreadsheet.InitializeSpreadsheetControl("container-id");
+```
+
+## ๐ฑ Platform Support
+
+- **Web Browsers**: Chrome, Firefox, Safari, Edge, Internet Explorer 8+
+- **Operating Systems**: Windows, macOS, Linux (via web browser)
+- **Mobile**: iOS Safari, Android Chrome (responsive design)
+- **Server Platforms**: Linux, Unix, Windows (for Perl backend)
+
+## ๐งช Testing
+
+```bash
+# Run basic functionality tests
+# Open test files in browser:
+# - ssctrltest1.html (Spreadsheet control test)
+# - socialcalc2demo-0-8-1.html (Demo application)
+# - socialcalc2demo10.html (Advanced demo)
+
+# Manual testing checklist:
+# 1. Cell editing and navigation
+# 2. Formula calculation
+# 3. Copy/paste operations
+# 4. Formatting functions
+# 5. Undo/redo functionality
+```
+
+## ๐ Deployment
+
+### Static Deployment
+```bash
+# Deploy to Apache/Nginx
+sudo cp -r socialcalc/ /var/www/html/
+sudo chown -R www-data:www-data /var/www/html/socialcalc/
+
+# Configure MIME types for .js and .css files
+# Ensure proper caching headers are set
+```
+
+### Server-side Features
+```bash
+# Configure Perl CGI (optional)
+# Enable mod_cgi or mod_cgid in Apache
+# Set up proper permissions for .pl scripts
+
+# Test Perl components
+perl -c simpleedit14.pl
+perl -c simpleedit15.pl
+perl -c socialcalcserver.pl
+```
+
+## ๐ Performance & Optimization
+
+- **Modular Loading**: Load only required JavaScript modules
+- **Image Optimization**: UI images are optimized GIF format
+- **Client-side Processing**: Reduces server load with local calculations
+- **Efficient Rendering**: Optimized DOM manipulation for large spreadsheets
+- **Memory Management**: Proper cleanup of event handlers and objects
+
+## ๐ค Contributing
+
+We welcome contributions! Please follow these guidelines:
+
+1. Fork the repository
+2. Create a feature branch (`git checkout -b feature/amazing-feature`)
+3. Commit your changes (`git commit -m 'Add some amazing feature'`)
+4. Push to the branch (`git push origin feature/amazing-feature`)
+5. Open a Pull Request
+
+### Development Guidelines
+- Follow existing JavaScript coding style (ES5 compatible)
+- Test changes across multiple browsers
+- Update documentation for new features
+- Ensure Perl components remain functional
+- Maintain compatibility with existing APIs
+
+### Code Style
+- Use consistent indentation (spaces preferred)
+- Comment complex algorithms and formulas
+- Follow SocialCalc naming conventions
+- Test formula engine changes thoroughly
+
+## ๐ License
+
+This project is licensed under the ISC License. See the `LICENSE.txt` file for details.
+
+**Additional Legal Information:**
+- Copyright (C) 2009 Socialtext, Inc. All Rights Reserved
+- Based on original SocialCalc code licensed under Artistic License 2.0
+- Some components may be subject to different licenses (see `LEGAL.txt`)
+- SocialCalc and related trademarks are property of their respective owners
+
+## ๐ Acknowledgments
+
+- **Dan Bricklin** - Original SocialCalc architect and developer
+- **Socialtext, Inc.** - Original project sponsorship and development
+- **Software Garden, Inc.** - Foundation wikiCalc technology
+- **SocialCalc Community** - Ongoing development and maintenance
+
+## ๐ Support & Contact
+
+- **GitHub Issues**: [Report bugs and request features](https://github.com/anisharma07/socialcalc/issues)
+- **Original Project**: [SocialCalc.org](http://socialcalc.org/)
+- **Documentation**: Refer to source code comments and `Changes.txt` for detailed information
+- **Community**: Join discussions on spreadsheet technology and collaborative editing
+
+---
+
+**Note**: This is a historical preservation of the SocialCalc project. For production use, consider modern alternatives or significant updates to support current web standards and security practices.
\ No newline at end of file
diff --git a/.github/claude/security-audit-report.md b/.github/claude/security-audit-report.md
new file mode 100644
index 0000000..b792bf6
--- /dev/null
+++ b/.github/claude/security-audit-report.md
@@ -0,0 +1,213 @@
+# ๐ Security & Code Quality Audit Report
+
+**Repository:** anisharma07/socialcalc
+**Audit Date:** 2025-07-30 13:39:27
+**Scope:** Comprehensive security and code quality analysis
+
+## ๐ Executive Summary
+
+The security audit of the socialcalc repository reveals a mixed security posture with **significant GitHub Actions security vulnerabilities** and **critical JavaScript parsing issues**. While the project shows no direct npm vulnerabilities or Python security issues, there are 53 static analysis findings from Semgrep that require immediate attention, particularly around command injection risks in CI/CD workflows.
+
+The codebase consists of 26,291 lines across 28 files, primarily JavaScript (16,763 LOC), with additional Perl, YAML, and HTML components. The diversity of languages and the presence of formula evaluation code introduces complexity that requires careful security consideration.
+
+### Risk Assessment
+- **Critical Issues:** 2 (GitHub Actions Command Injection vulnerabilities)
+- **Major Issues:** 2 (JavaScript parsing errors, potential eval usage)
+- **Minor Issues:** 49+ (Various Semgrep static analysis findings)
+- **Overall Risk Level:** **HIGH** - Immediate action required for CI/CD security
+
+## ๐จ Critical Security Issues
+
+### 1. GitHub Actions Command Injection Vulnerability
+- **Severity:** Critical
+- **Category:** Security - Command Injection (CWE-78)
+- **Description:** Multiple GitHub Actions workflows use unsafe variable interpolation with `${{...}}` syntax and `github` context data in `run:` steps. This creates a high-risk command injection vulnerability where attackers could inject malicious code into the runner environment.
+- **Impact:**
+ - Complete compromise of CI/CD environment
+ - Potential theft of secrets and source code
+ - Ability to modify builds and deployments
+ - Supply chain attack vector
+- **Location:**
+ - `.github/workflows/claude-audit.yml` (lines 829-848)
+ - `.github/workflows/claude-generate.yml` (lines 64-81)
+- **Remediation:**
+ 1. Replace direct `${{...}}` interpolation with environment variables
+ 2. Use `env:` section to store GitHub context data
+ 3. Quote environment variables in shell commands: `"$ENVVAR"`
+ 4. Implement input validation for all external data
+
+### 2. JavaScript Parsing Errors - Potential Security Code Issues
+- **Severity:** Critical
+- **Category:** Security - Code Quality
+- **Description:** Critical JavaScript files contain syntax that prevents proper parsing by security analysis tools, specifically binding 'eval' in strict mode and improper delete operations.
+- **Impact:**
+ - Unable to perform complete security analysis
+ - Potential unsafe eval usage
+ - Code execution vulnerabilities
+ - Maintenance and debugging difficulties
+- **Location:**
+ - `/formula1.js` (line 4094:33) - "Binding 'eval' in strict mode"
+ - `/socialcalc-3.js` (line 5565:6) - "Deleting local variable in strict mode"
+- **Remediation:**
+ 1. Audit all eval usage and replace with safer alternatives
+ 2. Fix delete operations to comply with strict mode
+ 3. Implement Content Security Policy to prevent code injection
+ 4. Use AST-based parsing instead of eval for formulas
+
+## โ ๏ธ Major Issues
+
+### 1. Incomplete Static Analysis Coverage
+- **Severity:** Major
+- **Category:** Code Quality
+- **Description:** 53 Semgrep findings were detected, but the provided data is truncated, preventing full assessment of all security issues.
+- **Impact:** Unknown security vulnerabilities may exist in the codebase
+- **Location:** Various files across the project
+- **Remediation:**
+ 1. Run complete Semgrep analysis with full output
+ 2. Address all findings systematically
+ 3. Integrate Semgrep into CI/CD pipeline
+
+### 2. Outdated Dependencies
+- **Severity:** Major
+- **Category:** Security - Dependency Management
+- **Description:** 6 retired/outdated dependencies identified, though specific details weren't provided in the audit data.
+- **Impact:**
+ - Known security vulnerabilities in older versions
+ - Missing security patches
+ - Compatibility issues
+- **Location:** Package dependencies
+- **Remediation:**
+ 1. Update all dependencies to latest stable versions
+ 2. Implement automated dependency scanning
+ 3. Establish regular dependency update schedule
+
+## ๐ Minor Issues & Improvements
+
+### Code Quality Observations
+1. **Large JavaScript Files**: Core files contain substantial LOC (16,763 total), suggesting potential for modularization
+2. **Mixed Technology Stack**: Perl, JavaScript, YAML, HTML - requires diverse security expertise
+3. **Formula Processing**: Spreadsheet formula evaluation presents inherent security risks
+4. **Comment Density**: Good documentation with 4,228 comment lines (16% of total)
+
+## ๐ Dead Code Analysis
+
+### Unused Dependencies
+- **Status**: Clean npm dependency state with only 1 production dependency
+- **No unused packages** identified in current scan
+- **Recommendation**: Maintain this lean dependency approach
+
+### Unused Code
+- **JavaScript Parsing Issues**: Unable to complete full dead code analysis due to syntax errors
+- **Action Required**: Fix parsing errors before conducting thorough dead code analysis
+
+### Unused Imports
+- **Status**: Cannot assess due to JavaScript parsing failures
+- **Recommendation**: Address syntax issues first, then run comprehensive import analysis
+
+## ๐ Refactoring Suggestions
+
+### Code Quality Improvements
+1. **Modularize Large JavaScript Files**: Break down 16,763 LOC into smaller, focused modules
+2. **Implement ES6+ Standards**: Modernize JavaScript codebase for better maintainability
+3. **Strict Mode Compliance**: Ensure all JavaScript follows strict mode requirements
+4. **Type Safety**: Consider TypeScript migration for better type safety
+
+### Performance Optimizations
+1. **Formula Evaluation**: Implement safer, more efficient formula parsing
+2. **Memory Management**: Review large data structure handling in spreadsheet operations
+3. **Caching Strategies**: Implement caching for frequently calculated formulas
+
+### Architecture Improvements
+1. **Separation of Concerns**: Isolate formula evaluation from UI components
+2. **Security Boundaries**: Implement clear security boundaries around eval-like operations
+3. **Error Handling**: Improve error handling throughout the application
+
+## ๐ก๏ธ Security Recommendations
+
+### Vulnerability Remediation (Priority Order)
+1. **Fix GitHub Actions Command Injection** - Critical, immediate action required
+2. **Resolve JavaScript Parsing Issues** - Blocks security analysis
+3. **Complete Semgrep Analysis** - Address all 53 findings
+4. **Update Dependencies** - Eliminate known vulnerabilities
+
+### Security Best Practices
+1. **Content Security Policy**: Implement CSP headers to prevent code injection
+2. **Input Validation**: Validate all formula inputs and user data
+3. **Sandboxing**: Isolate formula evaluation in secure sandbox
+4. **Access Controls**: Implement proper authentication and authorization
+5. **Audit Logging**: Add comprehensive security event logging
+
+### Dependency Management
+1. **Automated Scanning**: Implement Dependabot or similar for dependency updates
+2. **Vulnerability Monitoring**: Set up alerts for new vulnerabilities
+3. **Lock File Management**: Maintain proper package-lock.json
+4. **Supply Chain Security**: Verify dependency integrity and sources
+
+## ๐ง Development Workflow Improvements
+
+### Static Analysis Integration
+1. **Semgrep CI Integration**: Add Semgrep to GitHub Actions workflow
+2. **ESLint Setup**: Configure comprehensive ESLint rules
+3. **Security Linting**: Implement security-focused linting rules
+4. **Pre-commit Hooks**: Add security checks to pre-commit process
+
+### Security Testing
+1. **SAST Integration**: Continuous static application security testing
+2. **Dependency Scanning**: Automated dependency vulnerability scanning
+3. **Secret Scanning**: Prevent credential commits
+4. **Penetration Testing**: Regular security assessments
+
+### Code Quality Gates
+1. **Security Threshold**: Block builds with critical security issues
+2. **Code Coverage**: Maintain minimum test coverage requirements
+3. **Complexity Limits**: Set cyclomatic complexity thresholds
+4. **Documentation Requirements**: Ensure security-sensitive code is documented
+
+## ๐ Action Items
+
+### Immediate Actions (Next 1-2 weeks)
+1. **FIX CRITICAL**: Remediate GitHub Actions command injection vulnerabilities
+2. **FIX CRITICAL**: Resolve JavaScript parsing errors in formula1.js and socialcalc-3.js
+3. **SECURITY**: Complete full Semgrep analysis and review all findings
+4. **AUDIT**: Conduct comprehensive review of eval usage throughout codebase
+
+### Short-term Actions (Next month)
+1. **UPDATE**: Upgrade all outdated dependencies
+2. **IMPLEMENT**: Add Semgrep and ESLint to CI/CD pipeline
+3. **SECURITY**: Implement Content Security Policy
+4. **TESTING**: Add automated security testing to workflows
+5. **DOCUMENTATION**: Document all security-sensitive functionality
+
+### Long-term Actions (Next quarter)
+1. **REFACTOR**: Modularize large JavaScript files
+2. **MODERNIZE**: Consider TypeScript migration
+3. **ARCHITECTURE**: Implement secure formula evaluation sandbox
+4. **MONITORING**: Set up comprehensive security monitoring
+5. **TRAINING**: Conduct security awareness training for development team
+
+## ๐ Metrics & Tracking
+
+### Current Status
+- **Total Issues:** 55+
+- **Critical:** 2
+- **Major:** 2
+- **Minor:** 51+
+
+### Progress Tracking
+- **Weekly Security Reviews**: Track resolution of critical and major issues
+- **Dependency Health Dashboard**: Monitor dependency update status
+- **Security Metrics**: Track SAST findings, dependency vulnerabilities, and code quality scores
+- **CI/CD Security Gates**: Measure build success rates with security checks enabled
+
+## ๐ Resources & References
+
+- [GitHub Actions Security Hardening Guide](https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions)
+- [OWASP Secure Coding Practices](https://owasp.org/www-project-secure-coding-practices-quick-reference-guide/)
+- [Semgrep Rule Documentation](https://semgrep.dev/docs/)
+- [JavaScript Security Best Practices](https://cheatsheetseries.owasp.org/cheatsheets/JavaScript_Security_Cheat_Sheet.html)
+- [CWE-78: OS Command Injection](https://cwe.mitre.org/data/definitions/78.html)
+- [Supply Chain Security Framework](https://slsa.dev/)
+
+---
+
+**โ ๏ธ URGENT**: The GitHub Actions command injection vulnerabilities pose an immediate and severe security risk. These must be addressed before any CI/CD workflows are executed to prevent potential compromise of the development environment and supply chain.
\ No newline at end of file
diff --git a/.github/claude/tests/README.md b/.github/claude/tests/README.md
new file mode 100644
index 0000000..fb87204
--- /dev/null
+++ b/.github/claude/tests/README.md
@@ -0,0 +1,151 @@
+# Claude AI Generated Tests
+
+**Generated:** 2025-07-30 13:40:53
+**Test Type:** all
+**Framework:** jest
+**Coverage Target:** 80%
+
+## ๐ Overview
+
+This directory contains comprehensive tests generated by Claude AI for the socialcalc project.
+The tests are designed to provide good coverage and catch real bugs in your application.
+
+## ๐งช Generated Test Files
+
+- `formatnumber2.test.js` - Application tests
+- `formula1.test.js` - Application tests
+- `socialcalc-3.test.js` - Application tests
+- `socialcalcconstants.test.js` - Application tests
+- `socialcalcpopup.test.js` - Application tests
+- `socialcalcspreadsheetcontrol.test.js` - Application tests
+- `socialcalctableeditor.test.js` - Application tests
+
+
+## ๐ Running Tests
+
+### Prerequisites
+
+Make sure you have the necessary testing dependencies installed:
+
+```bash
+npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event jest jest-environment-jsdom
+```
+
+### Running Tests in This Directory Only
+
+To run only the tests in this directory:
+
+```bash
+# Run all tests in this directory
+npx jest .github/claude/tests
+
+# Run tests with coverage
+npx jest .github/claude/tests --coverage
+
+# Run tests in watch mode
+npx jest .github/claude/tests --watch
+```
+
+### Running Specific Test Files
+
+```bash
+# Run a specific test file
+npx jest .github/claude/tests/specific-test.test.ts
+```
+
+## ๐ Test Coverage
+
+Target coverage: **80%**
+
+To check coverage for these tests:
+
+```bash
+npx jest .github/claude/tests --coverage
+```
+
+## ๐ง Test Configuration
+
+### Framework: jest
+
+Jest is a JavaScript testing framework with a focus on simplicity.
+- Uses describe() and test() functions to structure tests
+- Built-in assertion library with expect()
+- Powerful mocking capabilities with jest.mock()
+- Snapshot testing for React components
+
+## ๐ Test Structure
+
+Each test file follows these conventions:
+
+- **Descriptive test names** that explain what is being tested
+- **Proper setup and teardown** for clean test environments
+- **Mocked dependencies** for isolated testing
+- **Edge case testing** for robust error handling
+- **Clear assertions** that validate expected behavior
+
+## ๐ฏ Test Types Generated
+
+
+- **Unit Tests**: Testing individual functions and methods in isolation
+- **Component Tests**: Testing React components with user interactions
+- **Integration Tests**: Testing component interactions and data flow
+- **Service Tests**: Testing API calls and business logic
+
+
+## ๐ Maintenance
+
+### Adding New Tests
+
+1. Follow the existing naming conventions
+2. Place tests in appropriate subdirectories
+3. Use the same testing patterns and setup
+4. Update this README if you add new categories
+
+### Updating Tests
+
+When source code changes:
+
+1. Review related test files
+2. Update test cases as needed
+3. Ensure coverage targets are maintained
+4. Run the full test suite to ensure no regressions
+
+## ๐ Debugging Tests
+
+### Common Issues
+
+1. **Import errors**: Check that all dependencies are installed
+2. **Mock failures**: Verify that mocks match the actual API
+3. **Async issues**: Ensure proper async/await handling
+4. **Environment setup**: Check test environment configuration
+
+### Debug Commands
+
+```bash
+# Run tests with verbose output
+npx jest --verbose
+
+# Run tests with debugging
+node --inspect-brk node_modules/.bin/jest --runInBand
+```
+
+## ๐ Resources
+
+- [jest Documentation](https://example.com)
+- [Testing Best Practices](https://example.com)
+- [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
+- [Ionic Testing Guide](https://ionicframework.com/docs/testing/overview)
+
+## ๐ค Contributing
+
+When contributing new tests:
+
+1. Follow the existing patterns and conventions
+2. Ensure tests are independent and can run in any order
+3. Use descriptive test names and clear assertions
+4. Include both positive and negative test cases
+5. Mock external dependencies appropriately
+
+---
+
+*Tests generated by Claude AI - Review and customize as needed for your specific requirements*
diff --git a/.github/claude/tests/formatnumber2.test.js b/.github/claude/tests/formatnumber2.test.js
new file mode 100644
index 0000000..f522c32
--- /dev/null
+++ b/.github/claude/tests/formatnumber2.test.js
@@ -0,0 +1,116 @@
+const fs = require('fs');
+const path = require('path');
+
+// Load the SocialCalc modules
+const formatNumberCode = fs.readFileSync(path.join(__dirname, '../../formatnumber2.js'), 'utf8');
+eval(formatNumberCode);
+
+describe('SocialCalc.FormatNumber', () => {
+ beforeEach(() => {
+ // Reset format definitions before each test
+ if (global.SocialCalc && global.SocialCalc.FormatNumber) {
+ global.SocialCalc.FormatNumber.format_definitions = {};
+ }
+ });
+
+ describe('formatNumberWithFormat', () => {
+ it('should format a positive number correctly', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(123.45, '#,##0.00', '$');
+ expect(result).toContain('123.45');
+ });
+
+ it('should format a negative number correctly', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(-123.45, '#,##0.00', '$');
+ expect(result).toContain('123.45');
+ });
+
+ it('should handle zero values', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(0, '#,##0.00', '$');
+ expect(result).toContain('0.00');
+ });
+
+ it('should handle very large numbers', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(1234567.89, '#,##0.00', '$');
+ expect(result).toContain('1,234,567.89');
+ });
+
+ it('should handle very small numbers', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(0.001, '0.000', '$');
+ expect(result).toContain('0.001');
+ });
+
+ it('should return NaN for invalid input', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(NaN, '#,##0.00', '$');
+ expect(result).toBe('NaN');
+ });
+
+ it('should handle infinity values', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(Infinity, '#,##0.00', '$');
+ expect(result).toBe('NaN');
+ });
+
+ it('should format currency correctly', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(1000, '$#,##0.00', '$');
+ expect(result).toContain('1,000.00');
+ });
+
+ it('should format percentages correctly', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(0.25, '0.00%', '$');
+ expect(result).toContain('25.00');
+ });
+
+ it('should handle date formatting', () => {
+ const dateValue = 44197; // Excel date serial for 2021-01-01
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(dateValue, 'mm/dd/yyyy', '$');
+ expect(typeof result).toBe('string');
+ expect(result.length).toBeGreaterThan(0);
+ });
+ });
+
+ describe('constants', () => {
+ it('should have correct separator and decimal characters', () => {
+ expect(SocialCalc.FormatNumber.separatorchar).toBe(',');
+ expect(SocialCalc.FormatNumber.decimalchar).toBe('.');
+ });
+
+ it('should have correct day names', () => {
+ expect(SocialCalc.FormatNumber.daynames).toHaveLength(7);
+ expect(SocialCalc.FormatNumber.daynames[0]).toBe('Sunday');
+ expect(SocialCalc.FormatNumber.daynames[6]).toBe('Saturday');
+ });
+
+ it('should have correct month names', () => {
+ expect(SocialCalc.FormatNumber.monthnames).toHaveLength(12);
+ expect(SocialCalc.FormatNumber.monthnames[0]).toBe('January');
+ expect(SocialCalc.FormatNumber.monthnames[11]).toBe('December');
+ });
+
+ it('should have allowed colors defined', () => {
+ expect(SocialCalc.FormatNumber.allowedcolors.BLACK).toBe('#000000');
+ expect(SocialCalc.FormatNumber.allowedcolors.WHITE).toBe('#FFFFFF');
+ expect(SocialCalc.FormatNumber.allowedcolors.RED).toBe('#FF0000');
+ });
+ });
+
+ describe('edge cases', () => {
+ it('should handle string input that can be converted to number', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat('123.45', '#,##0.00', '$');
+ expect(result).toContain('123.45');
+ });
+
+ it('should handle null input', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(null, '#,##0.00', '$');
+ expect(result).toContain('0.00');
+ });
+
+ it('should handle undefined input', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(undefined, '#,##0.00', '$');
+ expect(result).toBe('NaN');
+ });
+
+ it('should handle empty format string', () => {
+ const result = SocialCalc.FormatNumber.formatNumberWithFormat(123.45, '', '$');
+ expect(typeof result).toBe('string');
+ });
+ });
+});
\ No newline at end of file
diff --git a/.github/claude/tests/formula1.test.js b/.github/claude/tests/formula1.test.js
new file mode 100644
index 0000000..f817146
--- /dev/null
+++ b/.github/claude/tests/formula1.test.js
@@ -0,0 +1,130 @@
+const fs = require('fs');
+const path = require('path');
+
+// Load the SocialCalc modules
+const formulaCode = fs.readFileSync(path.join(__dirname, '../../formula1.js'), 'utf8');
+eval(formulaCode);
+
+describe('SocialCalc.Formula', () => {
+ beforeEach(() => {
+ // Ensure SocialCalc namespace exists
+ if (!global.SocialCalc) {
+ global.SocialCalc = {};
+ }
+ if (!global.SocialCalc.Constants) {
+ global.SocialCalc.Constants = {};
+ }
+ });
+
+ describe('ParseState constants', () => {
+ it('should have correct parse state values', () => {
+ expect(SocialCalc.Formula.ParseState.num).toBe(1);
+ expect(SocialCalc.Formula.ParseState.alpha).toBe(2);
+ expect(SocialCalc.Formula.ParseState.coord).toBe(3);
+ expect(SocialCalc.Formula.ParseState.string).toBe(4);
+ });
+ });
+
+ describe('TokenType constants', () => {
+ it('should have correct token type values', () => {
+ expect(SocialCalc.Formula.TokenType.num).toBe(1);
+ expect(SocialCalc.Formula.TokenType.coord).toBe(2);
+ expect(SocialCalc.Formula.TokenType.op).toBe(3);
+ expect(SocialCalc.Formula.TokenType.name).toBe(4);
+ });
+ });
+
+ describe('CharClass constants', () => {
+ it('should have correct character class values', () => {
+ expect(SocialCalc.Formula.CharClass.num).toBe(1);
+ expect(SocialCalc.Formula.CharClass.alpha).toBe(5);
+ expect(SocialCalc.Formula.CharClass.op).toBe(3);
+ });
+ });
+
+ describe('CharClassTable', () => {
+ it('should correctly classify numeric characters', () => {
+ expect(SocialCalc.Formula.CharClassTable['0']).toBe(1);
+ expect(SocialCalc.Formula.CharClassTable['9']).toBe(1);
+ expect(SocialCalc.Formula.CharClassTable['.']).toBe(2);
+ });
+
+ it('should correctly classify alphabetic characters', () => {
+ expect(SocialCalc.Formula.CharClassTable['A']).toBe(5);
+ expect(SocialCalc.Formula.CharClassTable['Z']).toBe(5);
+ expect(SocialCalc.Formula.CharClassTable['a']).toBe(5);
+ expect(SocialCalc.Formula.CharClassTable['z']).toBe(5);
+ });
+
+ it('should correctly classify operators', () => {
+ expect(SocialCalc.Formula.CharClassTable['+']).toBe(3);
+ expect(SocialCalc.Formula.CharClassTable['-']).toBe(3);
+ expect(SocialCalc.Formula.CharClassTable['*']).toBe(3);
+ expect(SocialCalc.Formula.CharClassTable['/']).toBe(3);
+ });
+
+ it('should correctly classify special characters', () => {
+ expect(SocialCalc.Formula.CharClassTable['"']).toBe(8);
+ expect(SocialCalc.Formula.CharClassTable[' ']).toBe(9);
+ expect(SocialCalc.Formula.CharClassTable['$']).toBe(6);
+ });
+ });
+
+ describe('UpperCaseTable', () => {
+ it('should correctly map lowercase to uppercase', () => {
+ expect(SocialCalc.Formula.UpperCaseTable['a']).toBe('A');
+ expect(SocialCalc.Formula.UpperCaseTable['z']).toBe('Z');
+ expect(SocialCalc.Formula.UpperCaseTable['m']).toBe('M');
+ });
+ });
+
+ describe('SpecialConstants', () => {
+ it('should have error constants defined', () => {
+ expect(SocialCalc.Formula.SpecialConstants['#NULL!']).toBe('0,e#NULL!');
+ expect(SocialCalc.Formula.SpecialConstants['#NUM!']).toBe('0,e#NUM!');
+ expect(SocialCalc.Formula.SpecialConstants['#DIV/0!']).toBe('0,e#DIV/0!');
+ expect(SocialCalc.Formula.SpecialConstants['#VALUE!']).toBe('0,e#VALUE!');
+ expect(SocialCalc.Formula.SpecialConstants['#REF!']).toBe('0,e#REF!');
+ expect(SocialCalc.Formula.SpecialConstants['#NAME?']).toBe('0,e#NAME?');
+ });
+ });
+
+ describe('TokenPrecedence', () => {
+ it('should have correct operator precedence', () => {
+ expect(SocialCalc.Formula.TokenPrecedence['!']).toBe(1);
+ expect(SocialCalc.Formula.TokenPrecedence[':']).toBe(2);
+ expect(SocialCalc.Formula.TokenPrecedence[',']).toBe(2);
+ expect(SocialCalc.Formula.TokenPrecedence['%']).toBe(4);
+ expect(SocialCalc.Formula.TokenPrecedence['^']).toBe(5);
+ });
+
+ it('should have right associative operators with negative precedence', () => {
+ expect(SocialCalc.Formula.TokenPrecedence['M']).toBe(-3);
+ expect(SocialCalc.Formula.TokenPrecedence['P']).toBe(-3);
+ });
+ });
+
+ describe('TokenOpExpansion', () => {
+ it('should correctly expand operator tokens', () => {
+ expect(SocialCalc.Formula.TokenOpExpansion['G']).toBe('>=');
+ expect(SocialCalc.Formula.TokenOpExpansion['L']).toBe('<=');
+ expect(SocialCalc.Formula.TokenOpExpansion['M']).toBe('-');
+ expect(SocialCalc.Formula.TokenOpExpansion['N']).toBe('<>');
+ expect(SocialCalc.Formula.TokenOpExpansion['P']).toBe('+');
+ });
+ });
+
+ describe('Formula parsing edge cases', () => {
+ it('should handle invalid character classes gracefully', () => {
+ const invalidChar = String.fromCharCode(256);
+ expect(SocialCalc.Formula.CharClassTable[invalidChar]).toBeUndefined();
+ });
+
+ it('should handle all defined operator precedences', () => {
+ const operators = Object.keys(SocialCalc.Formula.TokenPrecedence);
+ operators.forEach(op => {
+ expect(typeof SocialCalc.Formula.TokenPrecedence[op]).toBe('number');
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/.github/claude/tests/socialcalc-3.test.js b/.github/claude/tests/socialcalc-3.test.js
new file mode 100644
index 0000000..edfddff
--- /dev/null
+++ b/.github/claude/tests/socialcalc-3.test.js
@@ -0,0 +1,183 @@
+const fs = require('fs');
+const path = require('path');
+
+// Mock DOM methods
+global.document = {
+ createElement: jest.fn(() => ({
+ style: {},
+ appendChild: jest.fn(),
+ setAttribute: jest.fn(),
+ getAttribute: jest.fn(),
+ removeChild: jest.fn()
+ })),
+ getElementById: jest.fn(),
+ getElementsByTagName: jest.fn(() => []),
+ body: {
+ appendChild: jest.fn()
+ }
+};
+
+global.window = {
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn()
+};
+
+// Load the main SocialCalc module
+const socialCalcCode = fs.readFileSync(path.join(__dirname, '../../socialcalc-3.js'), 'utf8');
+eval(socialCalcCode);
+
+describe('SocialCalc Main Module', () => {
+ beforeEach(() => {
+ // Reset SocialCalc state
+ if (global.SocialCalc) {
+ global.SocialCalc.CurrentSpreadsheetControlObject = null;
+ }
+ });
+
+ describe('SocialCalc namespace', () => {
+ it('should exist as a global object', () => {
+ expect(typeof SocialCalc).toBe('object');
+ expect(SocialCalc).not.toBeNull();
+ });
+
+ it('should have version information', () => {
+ expect(typeof SocialCalc.version).toBeDefined();
+ });
+ });
+
+ describe('SocialCalc.Sheet', () => {
+ let sheet;
+
+ beforeEach(() => {
+ if (typeof SocialCalc.Sheet === 'function') {
+ sheet = new SocialCalc.Sheet();
+ }
+ });
+
+ it('should create a new sheet object', () => {
+ if (sheet) {
+ expect(sheet).toBeDefined();
+ expect(typeof sheet).toBe('object');
+ }
+ });
+
+ it('should initialize with default properties', () => {
+ if (sheet) {
+ expect(sheet.cells).toBeDefined();
+ expect(sheet.attribs).toBeDefined();
+ expect(sheet.names).toBeDefined();
+ }
+ });
+ });
+
+ describe('SocialCalc.Cell', () => {
+ let cell;
+
+ beforeEach(() => {
+ if (typeof SocialCalc.Cell === 'function') {
+ cell = new SocialCalc.Cell('A1');
+ }
+ });
+
+ it('should create a new cell object', () => {
+ if (cell) {
+ expect(cell).toBeDefined();
+ expect(typeof cell).toBe('object');
+ }
+ });
+
+ it('should have basic cell properties', () => {
+ if (cell) {
+ expect(cell.coord).toBeDefined();
+ expect(cell.datatype).toBeDefined();
+ expect(cell.datavalue).toBeDefined();
+ }
+ });
+ });
+
+ describe('Cell coordinate functions', () => {
+ it('should have coordinate utility functions', () => {
+ if (SocialCalc.coordToCr) {
+ expect(typeof SocialCalc.coordToCr).toBe('function');
+ }
+ if (SocialCalc.crToCoord) {
+ expect(typeof SocialCalc.crToCoord).toBe('function');
+ }
+ });
+
+ it('should convert coordinates correctly', () => {
+ if (SocialCalc.coordToCr && SocialCalc.crToCoord) {
+ const result = SocialCalc.coordToCr('A1');
+ if (result) {
+ expect(result.row).toBeDefined();
+ expect(result.col).toBeDefined();
+ }
+
+ const coord = SocialCalc.crToCoord(1, 1);
+ if (coord) {
+ expect(typeof coord).toBe('string');
+ }
+ }
+ });
+ });
+
+ describe('Error handling', () => {
+ it('should handle invalid inputs gracefully', () => {
+ if (SocialCalc.coordToCr) {
+ const result = SocialCalc.coordToCr('INVALID');
+ expect(result).toBeDefined(); // Should not throw
+ }
+ });
+ });
+
+ describe('RenderContext', () => {
+ let renderContext;
+
+ beforeEach(() => {
+ if (typeof SocialCalc.RenderContext === 'function') {
+ const sheet = new SocialCalc.Sheet();
+ renderContext = new SocialCalc.RenderContext(sheet);
+ }
+ });
+
+ it('should create a render context', () => {
+ if (renderContext) {
+ expect(renderContext).toBeDefined();
+ expect(typeof renderContext).toBe('object');
+ }
+ });
+
+ it('should have rendering methods', () => {
+ if (renderContext) {
+ expect(typeof renderContext.RenderSheet).toBe('function');
+ }
+ });
+ });
+
+ describe('Utility functions', () => {
+ it('should have string encoding/decoding functions', () => {
+ if (SocialCalc.encodeForSave) {
+ expect(typeof SocialCalc.encodeForSave).toBe('function');
+ }
+ if (SocialCalc.decodeFromSave) {
+ expect(typeof SocialCalc.decodeFromSave).toBe('function');
+ }
+ });
+
+ it('should handle empty strings correctly', () => {
+ if (SocialCalc.encodeForSave) {
+ const encoded = SocialCalc.encodeForSave('');
+ expect(typeof encoded).toBe('string');
+ }
+ });
+
+ it('should handle special characters', () => {
+ if (SocialCalc.encodeForSave && SocialCalc.decodeFromSave) {
+ const original = 'Test\nwith\tspecial\rchars';
+ const encoded = SocialCalc.encodeForSave(original);
+ const decoded = SocialCalc.decodeFromSave(encoded);
+ expect(decoded).toBe(original);
+ }
+ });
+ });
+});
\ No newline at end of file
diff --git a/.github/claude/tests/socialcalcconstants.test.js b/.github/claude/tests/socialcalcconstants.test.js
new file mode 100644
index 0000000..c151b07
--- /dev/null
+++ b/.github/claude/tests/socialcalcconstants.test.js
@@ -0,0 +1,115 @@
+const fs = require('fs');
+const path = require('path');
+
+// Load the constants module
+const constantsCode = fs.readFileSync(path.join(__dirname, '../../socialcalcconstants.js'), 'utf8');
+eval(constantsCode);
+
+describe('SocialCalc.Constants', () => {
+ it('should exist as an object', () => {
+ expect(typeof SocialCalc.Constants).toBe('object');
+ expect(SocialCalc.Constants).not.toBeNull();
+ });
+
+ describe('Common Constants', () => {
+ it('should have textdatadefaulttype defined', () => {
+ expect(SocialCalc.Constants.textdatadefaulttype).toBeDefined();
+ expect(typeof SocialCalc.Constants.textdatadefaulttype).toBe('string');
+ });
+ });
+
+ describe('Error messages', () => {
+ it('should have browser not supported message', () => {
+ expect(SocialCalc.Constants.s_BrowserNotSupported).toBeDefined();
+ expect(typeof SocialCalc.Constants.s_BrowserNotSupported).toBe('string');
+ expect(SocialCalc.Constants.s_BrowserNotSupported.length).toBeGreaterThan(0);
+ });
+
+ it('should have internal error message', () => {
+ expect(SocialCalc.Constants.s_InternalError).toBeDefined();
+ expect(typeof SocialCalc.Constants.s_InternalError).toBe('string');
+ expect(SocialCalc.Constants.s_InternalError.length).toBeGreaterThan(0);
+ });
+ });
+
+ describe('Parse error messages', () => {
+ it('should have unknown column type error', () => {
+ expect(SocialCalc.Constants.s_pssUnknownColType).toBeDefined();
+ expect(typeof SocialCalc.Constants.s_pssUnknownColType).toBe('string');
+ });
+
+ it('should have unknown row type error', () => {
+ expect(SocialCalc.Constants.s_pssUnknownRowType).toBeDefined();
+ expect(typeof SocialCalc.Constants.s_pssUnknownRowType).toBe('string');
+ });
+
+ it('should have unknown line type error', () => {
+ expect(SocialCalc.Constants.s_pssUnknownLineType).toBeDefined();
+ expect(typeof SocialCalc.Constants.s_pssUnknownLineType).toBe('string');
+ });
+ });
+
+ describe('Cell parsing errors', () => {
+ it('should have unknown cell type error', () => {
+ expect(SocialCalc.Constants.s_cfspUnknownCellType).toBeDefined();
+ expect(typeof SocialCalc.Constants.s_cfspUnknownCellType).toBe('string');
+ });
+ });
+
+ describe('String constants starting with s_', () => {
+ const stringConstants = Object.keys(SocialCalc.Constants).filter(key => key.startsWith('s_'));
+
+ it('should have multiple string constants for localization', () => {
+ expect(stringConstants.length).toBeGreaterThan(0);
+ });
+
+ it('should have all string constants as strings', () => {
+ stringConstants.forEach(key => {
+ expect(typeof SocialCalc.Constants[key]).toBe('string');
+ expect(SocialCalc.Constants[key].length).toBeGreaterThan(0);
+ });
+ });
+ });
+
+ describe('Non-string constants', () => {
+ const nonStringKeys = Object.keys(SocialCalc.Constants).filter(key => !key.startsWith('s_'));
+
+ it('should have configuration constants', () => {
+ expect(nonStringKeys.length).toBeGreaterThan(0);
+ });
+
+ it('should have textdatadefaulttype as expected value', () => {
+ expect(SocialCalc.Constants.textdatadefaulttype).toBe('t');
+ });
+ });
+
+ describe('Constants integrity', () => {
+ it('should not have null or undefined values', () => {
+ Object.keys(SocialCalc.Constants).forEach(key => {
+ expect(SocialCalc.Constants[key]).not.toBeNull();
+ expect(SocialCalc.Constants[key]).not.toBeUndefined();
+ });
+ });
+
+ it('should have consistent naming convention for error messages', () => {
+ const errorKeys = Object.keys(SocialCalc.Constants).filter(key =>
+ key.includes('Error') || key.includes('Unknown') || key.includes('NotSupported')
+ );
+
+ errorKeys.forEach(key => {
+ expect(key).toMatch(/^s_/);
+ });
+ });
+ });
+
+ describe('Tooltip constants', () => {
+ it('should handle tooltip offset constants if they exist', () => {
+ if (SocialCalc.Constants.TooltipOffsetX !== undefined) {
+ expect(typeof SocialCalc.Constants.TooltipOffsetX).toBe('number');
+ }
+ if (SocialCalc.Constants.TooltipOffsetY !== undefined) {
+ expect(typeof SocialCalc.Constants.TooltipOffsetY).toBe('number');
+ }
+ });
+ });
+});
\ No newline at end of file
diff --git a/.github/claude/tests/socialcalcpopup.test.js b/.github/claude/tests/socialcalcpopup.test.js
new file mode 100644
index 0000000..d798606
--- /dev/null
+++ b/.github/claude/tests/socialcalcpopup.test.js
@@ -0,0 +1,236 @@
+const fs = require('fs');
+const path = require('path');
+
+// Mock DOM environment
+global.document = {
+ createElement: jest.fn(() => ({
+ style: {},
+ appendChild: jest.fn(),
+ setAttribute: jest.fn(),
+ getAttribute: jest.fn(),
+ removeChild: jest.fn(),
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn()
+ })),
+ getElementById: jest.fn(() => ({
+ style: {},
+ appendChild: jest.fn(),
+ removeChild: jest.fn()
+ })),
+ getElementsByTagName: jest.fn(() => []),
+ body: {
+ appendChild: jest.fn()
+ }
+};
+
+// Load the popup module
+const popupCode = fs.readFileSync(path.join(__dirname, '../../socialcalcpopup.js'), 'utf8');
+eval(popupCode);
+
+describe('SocialCalc.Popup', () => {
+ beforeEach(() => {
+ // Reset popup state
+ if (global.SocialCalc && global.SocialCalc.Popup) {
+ global.SocialCalc.Popup.Controls = {};
+ global.SocialCalc.Popup.Current = {};
+ }
+ });
+
+ describe('Popup namespace', () => {
+ it('should exist as an object', () => {
+ expect(typeof SocialCalc.Popup).toBe('object');
+ expect(SocialCalc.Popup).not.toBeNull();
+ });
+
+ it('should have Types object', () => {
+ expect(typeof SocialCalc.Popup.Types).toBe('object');
+ });
+
+ it('should have Controls object', () => {
+ expect(typeof SocialCalc.Popup.Controls).toBe('object');
+ });
+
+ it('should have Current object', () => {
+ expect(typeof SocialCalc.Popup.Current).toBe('object');
+ });
+ });
+
+ describe('Configuration', () => {
+ it('should have imagePrefix defined', () => {
+ expect(SocialCalc.Popup.imagePrefix).toBeDefined();
+ expect(typeof SocialCalc.Popup.imagePrefix).toBe('string');
+ expect(SocialCalc.Popup.imagePrefix).toBe('images/sc-');
+ });
+
+ it('should have LocalizeString function', () => {
+ expect(typeof SocialCalc.Popup.LocalizeString).toBe('function');
+ });
+
+ it('should return input string by default in LocalizeString', () => {
+ const testString = 'Test String';
+ const result = SocialCalc.Popup.LocalizeString(testString);
+ expect(result).toBe(testString);
+ });
+ });
+
+ describe('Create function', () => {
+ it('should be defined', () => {
+ expect(typeof SocialCalc.Popup.Create).toBe('function');
+ });
+
+ it('should handle unknown type gracefully', () => {
+ expect(() => {
+ SocialCalc.Popup.Create('unknowntype', 'testid', {});
+ }).not.toThrow();
+ });
+
+ it('should call type-specific Create function if available', () => {
+ const mockCreate = jest.fn();
+ SocialCalc.Popup.Types['testtype'] = {
+ Create: mockCreate
+ };
+
+ SocialCalc.Popup.Create('testtype', 'testid', { test: 'value' });
+
+ expect(mockCreate).toHaveBeenCalledWith('testtype', 'testid', { test: 'value' });
+ });
+ });
+
+ describe('SetValue function', () => {
+ it('should be defined', () => {
+ expect(typeof SocialCalc.Popup.SetValue).toBe('function');
+ });
+
+ it('should handle unknown control gracefully', () => {
+ expect(() => {
+ SocialCalc.Popup.SetValue('unknownid', 'value');
+ }).not.toThrow();
+ });
+
+ it('should call type-specific SetValue function if available', () => {
+ const mockSetValue = jest.fn();
+ SocialCalc.Popup.Types['testtype'] = {
+ SetValue: mockSetValue
+ };
+ SocialCalc.Popup.Controls['testid'] = {
+ type: 'testtype'
+ };
+
+ SocialCalc.Popup.SetValue('testid', 'testvalue');
+
+ expect(mockSetValue).toHaveBeenCalledWith('testtype', 'testid', 'testvalue');
+ });
+ });
+
+ describe('GetValue function', () => {
+ it('should be defined', () => {
+ expect(typeof SocialCalc.Popup.GetValue).toBe('function');
+ });
+
+ it('should return empty string for unknown control', () => {
+ const result = SocialCalc.Popup.GetValue('unknownid');
+ expect(result).toBe('');
+ });
+
+ it('should call type-specific GetValue function if available', () => {
+ const mockGetValue = jest.fn().mockReturnValue('testreturn');
+ SocialCalc.Popup.Types['testtype'] = {
+ GetValue: mockGetValue
+ };
+ SocialCalc.Popup.Controls['testid'] = {
+ type: 'testtype'
+ };
+
+ const result = SocialCalc.Popup.GetValue('testid');
+
+ expect(mockGetValue).toHaveBeenCalledWith('testtype', 'testid');
+ expect(result).toBe('testreturn');
+ });
+ });
+
+ describe('SetDisabled function', () => {
+ it('should be defined', () => {
+ expect(typeof SocialCalc.Popup.SetDisabled).toBe('function');
+ });
+
+ it('should handle unknown control gracefully', () => {
+ expect(() => {
+ SocialCalc.Popup.SetDisabled('unknownid', true);
+ }).not.toThrow();
+ });
+
+ it('should call type-specific SetDisabled function if available', () => {
+ const mockSetDisabled = jest.fn();
+ SocialCalc.Popup.Types['testtype'] = {
+ SetDisabled: mockSetDisabled
+ };
+ SocialCalc.Popup.Controls['testid'] = {
+ type: 'testtype'
+ };
+
+ SocialCalc.Popup.SetDisabled('testid', true);
+
+ expect(mockSetDisabled).toHaveBeenCalledWith('testtype', 'testid', true);
+ });
+ });
+
+ describe('Show and Hide functions', () => {
+ it('should have Show function defined', () => {
+ expect(typeof SocialCalc.Popup.Show).toBe('function');
+ });
+
+ it('should have Hide function defined', () => {
+ expect(typeof SocialCalc.Popup.Hide).toBe('function');
+ });
+
+ it('should handle Show with unknown control gracefully', () => {
+ expect(() => {
+ SocialCalc.Popup.Show('unknownid');
+ }).not.toThrow();
+ });
+
+ it('should handle Hide with unknown control gracefully', () => {
+ expect(() => {
+ SocialCalc.Popup.Hide('unknownid');
+ }).not.toThrow();
+ });
+ });
+
+ describe('Control registration', () => {
+ it('should allow registering new control types', () => {
+ const testType = {
+ Create: jest.fn(),
+ SetValue: jest.fn(),
+ GetValue: jest.fn()
+ };
+
+ SocialCalc.Popup.Types['newtype'] = testType;
+
+ expect(SocialCalc.Popup.Types['newtype']).toBe(testType);
+ });
+
+ it('should allow registering control instances', () => {
+ const testControl = {
+ type: 'testtype',
+ value: 'testvalue',
+ data: {}
+ };
+
+ SocialCalc.Popup.Controls['newcontrol'] = testControl;
+
+ expect(SocialCalc.Popup.Controls['newcontrol']).toBe(testControl);
+ });
+ });
+
+ describe('Current state management', () => {
+ it('should track current control', () => {
+ SocialCalc.Popup.Current.id = 'testid';
+ expect(SocialCalc.Popup.Current.id).toBe('testid');
+ });
+
+ it('should allow clearing current control', () => {
+ SocialCalc.Popup.Current.id = null;
+ expect(SocialCalc.Popup.Current.id).toBeNull();
+ });
+ });
+});
\ No newline at end of file
diff --git a/.github/claude/tests/socialcalcspreadsheetcontrol.test.js b/.github/claude/tests/socialcalcspreadsheetcontrol.test.js
new file mode 100644
index 0000000..5d7d70d
--- /dev/null
+++ b/.github/claude/tests/socialcalcspreadsheetcontrol.test.js
@@ -0,0 +1,239 @@
+const fs = require('fs');
+const path = require('path');
+
+// Mock DOM and browser APIs
+global.document = {
+ createElement: jest.fn(() => ({
+ style: {},
+ appendChild: jest.fn(),
+ setAttribute: jest.fn(),
+ getAttribute: jest.fn(),
+ removeChild: jest.fn(),
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ innerHTML: '',
+ offsetHeight: 100,
+ offsetWidth: 100
+ })),
+ getElementById: jest.fn(() => ({
+ style: {},
+ appendChild: jest.fn(),
+ removeChild: jest.fn(),
+ offsetHeight: 500,
+ offsetWidth: 800
+ })),
+ getElementsByTagName: jest.fn(() => []),
+ body: {
+ appendChild: jest.fn()
+ }
+};
+
+global.window = {
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ setTimeout: jest.fn(),
+ clearTimeout: jest.fn()
+};
+
+// Load required modules in order
+const socialCalcCode = fs.readFileSync(path.join(__dirname, '../../socialcalc-3.js'), 'utf8');
+const tableEditorCode = fs.readFileSync(path.join(__dirname, '../../socialcalctableeditor.js'), 'utf8');
+const spreadsheetControlCode = fs.readFileSync(path.join(__dirname, '../../socialcalcspreadsheetcontrol.js'), 'utf8');
+
+eval(socialCalcCode);
+eval(tableEditorCode);
+eval(spreadsheetControlCode);
+
+describe('SocialCalc.SpreadsheetControl', () => {
+ let spreadsheetControl;
+ let mockElement;
+
+ beforeEach(() => {
+ mockElement = {
+ style: {},
+ appendChild: jest.fn(),
+ removeChild: jest.fn(),
+ offsetHeight: 500,
+ offsetWidth: 800,
+ innerHTML: ''
+ };
+
+ global.document.getElementById.mockReturnValue(mockElement);
+
+ if (typeof SocialCalc.SpreadsheetControl === 'function') {
+ spreadsheetControl = new SocialCalc.SpreadsheetControl();
+ }
+ });
+
+ describe('Constructor', () => {
+ it('should create a SpreadsheetControl instance', () => {
+ if (spreadsheetControl) {
+ expect(spreadsheetControl).toBeDefined();
+ expect(typeof spreadsheetControl).toBe('object');
+ }
+ });
+
+ it('should initialize with default properties', () => {
+ if (spreadsheetControl) {
+ expect(spreadsheetControl.sheet).toBeDefined();
+ expect(spreadsheetControl.editor).toBeDefined();
+ expect(spreadsheetControl.spreadsheetDiv).toBeDefined();
+ }
+ });
+ });
+
+ describe('InitializeSpreadsheetControl', () => {
+ it('should be a function', () => {
+ if (spreadsheetControl) {
+ expect(typeof spreadsheetControl.InitializeSpreadsheetControl).toBe('function');
+ }
+ });
+
+ it('should initialize with element ID', () => {
+ if (spreadsheetControl && spreadsheetControl.InitializeSpreadsheetControl) {
+ expect(() => {
+ spreadsheetControl.InitializeSpreadsheetControl('test-element', 500, 300);
+ }).not.toThrow();
+ }
+ });
+
+ it('should set dimensions correctly', () => {
+ if (spreadsheetControl && spreadsheetControl.InitializeSpreadsheetControl) {
+ spreadsheetControl.InitializeSpreadsheetControl('test-element', 600, 400);
+
+ if (spreadsheetControl.requestedHeight !== undefined) {
+ expect(spreadsheetControl.requestedHeight).toBe(400);
+ }
+ if (spreadsheetControl.requestedWidth !== undefined) {
+ expect(spreadsheetControl.requestedWidth).toBe(600);
+ }
+ }
+ });
+ });
+
+ describe('Sheet management', () => {
+ it('should have methods for sheet operations', () => {
+ if (spreadsheetControl) {
+ if (spreadsheetControl.CreateSheetSave) {
+ expect(typeof spreadsheetControl.CreateSheetSave).toBe('function');
+ }
+ if (spreadsheetControl.ParseSheetSave) {
+ expect(typeof spreadsheetControl.ParseSheetSave).toBe('function');
+ }
+ }
+ });
+
+ it('should handle empty sheet save', () => {
+ if (spreadsheetControl && spreadsheetControl.ParseSheetSave) {
+ expect(() => {
+ spreadsheetControl.ParseSheetSave('');
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Editor integration', () => {
+ it('should have editor property', () => {
+ if (spreadsheetControl) {
+ expect(spreadsheetControl.editor).toBeDefined();
+ }
+ });
+
+ it('should have methods for editor control', () => {
+ if (spreadsheetControl) {
+ if (spreadsheetControl.ExecuteCommand) {
+ expect(typeof spreadsheetControl.ExecuteCommand).toBe('function');
+ }
+ }
+ });
+ });
+
+ describe('Event handling', () => {
+ it('should handle resize events', () => {
+ if (spreadsheetControl && spreadsheetControl.DoOnResize) {
+ expect(typeof spreadsheetControl.DoOnResize).toBe('function');
+ expect(() => {
+ spreadsheetControl.DoOnResize();
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Layout and sizing', () => {
+ it('should handle layout computation', () => {
+ if (spreadsheetControl && spreadsheetControl.ComputeLayout) {
+ expect(typeof spreadsheetControl.ComputeLayout).toBe('function');
+ expect(() => {
+ spreadsheetControl.ComputeLayout();
+ }).not.toThrow();
+ }
+ });
+
+ it('should handle different size constraints', () => {
+ if (spreadsheetControl) {
+ expect(() => {
+ spreadsheetControl.InitializeSpreadsheetControl('test', 100, 100);
+ }).not.toThrow();
+
+ expect(() => {
+ spreadsheetControl.InitializeSpreadsheetControl('test', 1000, 800);
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Toolbar functionality', () => {
+ it('should have toolbar-related properties', () => {
+ if (spreadsheetControl) {
+ // Check for toolbar-related properties that might exist
+ const toolbarProps = ['toolbarDiv', 'formulabarDiv', 'statusbarDiv'];
+ toolbarProps.forEach(prop => {
+ if (spreadsheetControl[prop] !== undefined) {
+ expect(spreadsheetControl[prop]).toBeDefined();
+ }
+ });
+ }
+ });
+ });
+
+ describe('Error handling', () => {
+ it('should handle invalid element ID gracefully', () => {
+ global.document.getElementById.mockReturnValue(null);
+
+ if (spreadsheetControl) {
+ expect(() => {
+ spreadsheetControl.InitializeSpreadsheetControl('invalid-id', 500, 300);
+ }).not.toThrow();
+ }
+ });
+
+ it('should handle invalid dimensions', () => {
+ if (spreadsheetControl) {
+ expect(() => {
+ spreadsheetControl.InitializeSpreadsheetControl('test', -100, -100);
+ }).not.toThrow();
+
+ expect(() => {
+ spreadsheetControl.InitializeSpreadsheetControl('test', 0, 0);
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('State management', () => {
+ it('should maintain internal state correctly', () => {
+ if (spreadsheetControl) {
+ // Test that the control maintains its state
+ const initialState = spreadsheetControl.sheet;
+
+ // Perform some operation
+ if (spreadsheetControl.InitializeSpreadsheetControl) {
+ spreadsheetControl.InitializeSpreadsheetControl('test', 500, 300);
+ }
+
+ // State should still be accessible
+ expect(spreadsheetControl.sheet).toBeDefined();
+ }
+ });
+ });
+});
\ No newline at end of file
diff --git a/.github/claude/tests/socialcalctableeditor.test.js b/.github/claude/tests/socialcalctableeditor.test.js
new file mode 100644
index 0000000..33039ee
--- /dev/null
+++ b/.github/claude/tests/socialcalctableeditor.test.js
@@ -0,0 +1,325 @@
+const fs = require('fs');
+const path = require('path');
+
+// Mock DOM environment extensively
+global.document = {
+ createElement: jest.fn(() => ({
+ style: {},
+ appendChild: jest.fn(),
+ setAttribute: jest.fn(),
+ getAttribute: jest.fn(),
+ removeChild: jest.fn(),
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ innerHTML: '',
+ offsetHeight: 20,
+ offsetWidth: 100,
+ scrollLeft: 0,
+ scrollTop: 0,
+ clientHeight: 400,
+ clientWidth: 600
+ })),
+ getElementById: jest.fn(() => ({
+ style: {},
+ appendChild: jest.fn(),
+ removeChild: jest.fn(),
+ offsetHeight: 500,
+ offsetWidth: 800,
+ addEventListener: jest.fn()
+ })),
+ getElementsByTagName: jest.fn(() => []),
+ body: {
+ appendChild: jest.fn()
+ }
+};
+
+global.window = {
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ setTimeout: jest.fn((fn) => fn()),
+ clearTimeout: jest.fn(),
+ getSelection: jest.fn(() => ({
+ removeAllRanges: jest.fn()
+ }))
+};
+
+global.navigator = {
+ userAgent: 'Mozilla/5.0 (Test Browser)'
+};
+
+// Load required modules in order
+const socialCalcCode = fs.readFileSync(path.join(__dirname, '../../socialcalc-3.js'), 'utf8');
+const tableEditorCode = fs.readFileSync(path.join(__dirname, '../../socialcalctableeditor.js'), 'utf8');
+
+eval(socialCalcCode);
+eval(tableEditorCode);
+
+describe('SocialCalc.TableEditor', () => {
+ let tableEditor;
+ let mockSheet;
+
+ beforeEach(() => {
+ if (typeof SocialCalc.Sheet === 'function') {
+ mockSheet = new SocialCalc.Sheet();
+ }
+
+ if (typeof SocialCalc.TableEditor === 'function') {
+ tableEditor = new SocialCalc.TableEditor(mockSheet);
+ }
+ });
+
+ describe('Constructor', () => {
+ it('should create a TableEditor instance', () => {
+ if (tableEditor) {
+ expect(tableEditor).toBeDefined();
+ expect(typeof tableEditor).toBe('object');
+ }
+ });
+
+ it('should initialize with a sheet', () => {
+ if (tableEditor && mockSheet) {
+ expect(tableEditor.context).toBeDefined();
+ expect(tableEditor.context.sheet).toBe(mockSheet);
+ }
+ });
+
+ it('should initialize default properties', () => {
+ if (tableEditor) {
+ expect(tableEditor.toplevel).toBeDefined();
+ expect(tableEditor.griddiv).toBeDefined();
+ expect(tableEditor.fullgrid).toBeDefined();
+ }
+ });
+ });
+
+ describe('CreateTableEditor', () => {
+ it('should be a function', () => {
+ if (tableEditor) {
+ expect(typeof tableEditor.CreateTableEditor).toBe('function');
+ }
+ });
+
+ it('should create editor with dimensions', () => {
+ if (tableEditor && tableEditor.CreateTableEditor) {
+ expect(() => {
+ tableEditor.CreateTableEditor(500, 400);
+ }).not.toThrow();
+ }
+ });
+
+ it('should handle different sizes', () => {
+ if (tableEditor && tableEditor.CreateTableEditor) {
+ expect(() => {
+ tableEditor.CreateTableEditor(800, 600);
+ }).not.toThrow();
+
+ expect(() => {
+ tableEditor.CreateTableEditor(300, 200);
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Grid rendering', () => {
+ it('should have rendering context', () => {
+ if (tableEditor) {
+ expect(tableEditor.context).toBeDefined();
+ if (tableEditor.context) {
+ expect(typeof tableEditor.context.RenderSheet).toBe('function');
+ }
+ }
+ });
+
+ it('should handle grid updates', () => {
+ if (tableEditor && tableEditor.ScheduleRender) {
+ expect(typeof tableEditor.ScheduleRender).toBe('function');
+ expect(() => {
+ tableEditor.ScheduleRender();
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Cell selection', () => {
+ it('should have cursor management', () => {
+ if (tableEditor) {
+ expect(tableEditor.ecell).toBeDefined();
+ if (tableEditor.MoveECellWithKey) {
+ expect(typeof tableEditor.MoveECellWithKey).toBe('function');
+ }
+ }
+ });
+
+ it('should handle cell navigation', () => {
+ if (tableEditor && tableEditor.MoveECell) {
+ expect(typeof tableEditor.MoveECell).toBe('function');
+ expect(() => {
+ tableEditor.MoveECell('A1');
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Keyboard handling', () => {
+ it('should have keyboard event handlers', () => {
+ if (tableEditor) {
+ if (tableEditor.ProcessKey) {
+ expect(typeof tableEditor.ProcessKey).toBe('function');
+ }
+ }
+ });
+
+ it('should handle key events', () => {
+ if (tableEditor && tableEditor.ProcessKey) {
+ const mockEvent = {
+ keyCode: 65, // 'A' key
+ which: 65,
+ shiftKey: false,
+ ctrlKey: false,
+ preventDefault: jest.fn(),
+ stopPropagation: jest.fn()
+ };
+
+ expect(() => {
+ tableEditor.ProcessKey(mockEvent);
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Mouse handling', () => {
+ it('should handle mouse events', () => {
+ if (tableEditor && tableEditor.ProcessMouseEvent) {
+ expect(typeof tableEditor.ProcessMouseEvent).toBe('function');
+
+ const mockEvent = {
+ clientX: 100,
+ clientY: 100,
+ button: 0,
+ preventDefault: jest.fn(),
+ stopPropagation: jest.fn()
+ };
+
+ expect(() => {
+ tableEditor.ProcessMouseEvent(mockEvent, 'click');
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Scrolling', () => {
+ it('should have scroll handling methods', () => {
+ if (tableEditor) {
+ if (tableEditor.ScrollTableUpOneRow) {
+ expect(typeof tableEditor.ScrollTableUpOneRow).toBe('function');
+ }
+ if (tableEditor.ScrollTableDownOneRow) {
+ expect(typeof tableEditor.ScrollTableDownOneRow).toBe('function');
+ }
+ }
+ });
+
+ it('should handle scroll operations', () => {
+ if (tableEditor && tableEditor.ScrollTableUpOneRow) {
+ expect(() => {
+ tableEditor.ScrollTableUpOneRow();
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Editing mode', () => {
+ it('should handle edit mode transitions', () => {
+ if (tableEditor) {
+ if (tableEditor.StartCellEdit) {
+ expect(typeof tableEditor.StartCellEdit).toBe('function');
+ }
+ if (tableEditor.EndCellEdit) {
+ expect(typeof tableEditor.EndCellEdit).toBe('function');
+ }
+ }
+ });
+
+ it('should enter and exit edit mode', () => {
+ if (tableEditor && tableEditor.StartCellEdit && tableEditor.EndCellEdit) {
+ expect(() => {
+ tableEditor.StartCellEdit();
+ }).not.toThrow();
+
+ expect(() => {
+ tableEditor.EndCellEdit();
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Layout computation', () => {
+ it('should compute layout correctly', () => {
+ if (tableEditor && tableEditor.ComputeTableSkipData) {
+ expect(typeof tableEditor.ComputeTableSkipData).toBe('function');
+ expect(() => {
+ tableEditor.ComputeTableSkipData();
+ }).not.toThrow();
+ }
+ });
+
+ it('should handle resize events', () => {
+ if (tableEditor && tableEditor.ResizeTableEditor) {
+ expect(typeof tableEditor.ResizeTableEditor).toBe('function');
+ expect(() => {
+ tableEditor.ResizeTableEditor(600, 450);
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('Error handling', () => {
+ it('should handle null sheet gracefully', () => {
+ if (typeof SocialCalc.TableEditor === 'function') {
+ expect(() => {
+ new SocialCalc.TableEditor(null);
+ }).not.toThrow();
+ }
+ });
+
+ it('should handle invalid dimensions', () => {
+ if (tableEditor && tableEditor.CreateTableEditor) {
+ expect(() => {
+ tableEditor.CreateTableEditor(-100, -100);
+ }).not.toThrow();
+
+ expect(() => {
+ tableEditor.CreateTableEditor(0, 0);
+ }).not.toThrow();
+ }
+ });
+ });
+
+ describe('State management', () => {
+ it('should maintain editor state', () => {
+ if (tableEditor) {
+ const initialEcell = tableEditor.ecell;
+
+ // Perform operations
+ if (tableEditor.MoveECell) {
+ tableEditor.MoveECell('B2');
+ }
+
+ // State should be updated
+ if (tableEditor.ecell && initialEcell) {
+ expect(tableEditor.ecell).toBeDefined();
+ }
+ }
+ });
+ });
+
+ describe('Drag and drop', () => {
+ it('should have drag handling methods if available', () => {
+ if (tableEditor) {
+ if (tableEditor.ProcessMouseEvent) {
+ expect(typeof tableEditor.ProcessMouseEvent).toBe('function');
+ }
+ }
+ });
+ });
+});
\ No newline at end of file
diff --git a/.github/workflows/claude-audit.yml b/.github/workflows/claude-audit.yml
new file mode 100644
index 0000000..501eab3
--- /dev/null
+++ b/.github/workflows/claude-audit.yml
@@ -0,0 +1,848 @@
+name: Claude AI Security & Code Quality Audit
+
+on:
+ workflow_dispatch:
+ inputs:
+ include_security_scan:
+ description: "Include comprehensive security vulnerability scan"
+ required: false
+ default: true
+ type: boolean
+ include_dead_code_analysis:
+ description: "Include dead code and unused imports analysis"
+ required: false
+ default: true
+ type: boolean
+ include_refactor_suggestions:
+ description: "Include refactoring and optimization suggestions"
+ required: false
+ default: true
+ type: boolean
+ severity_threshold:
+ description: "Minimum severity level to report"
+ required: false
+ default: "minor"
+ type: choice
+ options:
+ - "critical"
+ - "major"
+ - "minor"
+ - "all"
+
+jobs:
+ security-audit:
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: write
+ pull-requests: write
+ actions: read
+ security-events: write
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Check and handle existing claude-audit branch
+ id: check-branch
+ run: |
+ if git ls-remote --heads origin claude-audit | grep -q claude-audit; then
+ echo "branch_exists=true" >> $GITHUB_OUTPUT
+ echo "โ ๏ธ Branch 'claude-audit' already exists!"
+ echo "๐๏ธ Automatically deleting existing branch..."
+ git push origin --delete claude-audit || echo "Branch deletion failed, but continuing..."
+ echo "โ
Existing branch deleted. Proceeding with audit generation."
+ else
+ echo "branch_exists=false" >> $GITHUB_OUTPUT
+ echo "โ
Branch 'claude-audit' does not exist. Proceeding with audit generation."
+ fi
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ${{ secrets.AWS_REGION }}
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "18"
+ cache: "npm"
+
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.11"
+
+ - name: Install dependencies and security tools
+ run: |
+ # Install Python dependencies
+ pip install boto3 requests botocore PyGithub bandit safety semgrep
+
+ # Install Node.js security tools
+ npm install -g audit-ci eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
+ npm install -g retire depcheck npm-audit-resolver
+
+ # Install GitHub CLI as backup for PR creation
+ type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
+ curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
+ && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
+ && sudo apt update \
+ && sudo apt install gh -y
+
+ # Install additional security analysis tools
+ sudo apt install -y tree file cloc shellcheck
+
+ - name: Run comprehensive security and code analysis
+ run: |
+ echo "๐ Running comprehensive security and code quality analysis..."
+
+ # Create output directory
+ mkdir -p audit_reports
+
+ # 1. Project structure analysis
+ echo "๐ Analyzing project structure..."
+ find . -type f \( \
+ -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o \
+ -name "*.py" -o -name "*.sql" -o -name "*.json" -o -name "*.yml" -o \
+ -name "*.yaml" -o -name "*.env*" -o -name "Dockerfile*" -o -name "*.sh" \
+ \) \
+ -not -path "./node_modules/*" \
+ -not -path "./.git/*" \
+ -not -path "./dist/*" \
+ -not -path "./build/*" \
+ -not -path "./coverage/*" > audit_reports/all_files.txt
+
+ echo "๐ Found $(wc -l < audit_reports/all_files.txt) files to analyze"
+
+ # 2. Code statistics
+ echo "๐ Generating code statistics..."
+ cloc . --exclude-dir=node_modules,dist,build,coverage,.git --json > audit_reports/code_stats.json 2>/dev/null || echo "{}" > audit_reports/code_stats.json
+
+ # 3. Node.js dependency vulnerabilities
+ echo "๐ Checking Node.js dependencies for vulnerabilities..."
+ if [ -f "package.json" ]; then
+ npm audit --json > audit_reports/npm_audit.json 2>/dev/null || echo "{}" > audit_reports/npm_audit.json
+ retire --outputformat json --outputpath audit_reports/retire_scan.json . 2>/dev/null || echo "{}" > audit_reports/retire_scan.json
+ else
+ echo "{}" > audit_reports/npm_audit.json
+ echo "{}" > audit_reports/retire_scan.json
+ fi
+
+ # 4. Python security analysis (if Python files exist)
+ echo "๐ Running Python security analysis..."
+ if find . -name "*.py" -not -path "./node_modules/*" | head -1 | grep -q .; then
+ bandit -r . -f json -o audit_reports/bandit_report.json 2>/dev/null || echo "{}" > audit_reports/bandit_report.json
+ safety check --json > audit_reports/safety_report.json 2>/dev/null || echo "{}" > audit_reports/safety_report.json
+ else
+ echo "{}" > audit_reports/bandit_report.json
+ echo "{}" > audit_reports/safety_report.json
+ fi
+
+ # 5. Static code analysis with Semgrep
+ echo "๐ Running Semgrep static analysis..."
+ semgrep --config=auto --json --output=audit_reports/semgrep_report.json . 2>/dev/null || echo "{}" > audit_reports/semgrep_report.json
+
+ # 6. ESLint analysis for JavaScript/TypeScript
+ echo "๐ Running ESLint analysis..."
+ if [ -f "package.json" ] && (find . -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" | head -1 | grep -q .); then
+ npx eslint . --ext .js,.jsx,.ts,.tsx --format json > audit_reports/eslint_report.json 2>/dev/null || echo "[]" > audit_reports/eslint_report.json
+ else
+ echo "[]" > audit_reports/eslint_report.json
+ fi
+
+ # 7. Dead code analysis
+ echo "๐ Analyzing dead code and unused dependencies..."
+ if [ -f "package.json" ]; then
+ depcheck --json > audit_reports/depcheck_report.json 2>/dev/null || echo "{}" > audit_reports/depcheck_report.json
+ else
+ echo "{}" > audit_reports/depcheck_report.json
+ fi
+
+ # 8. Shell script analysis
+ echo "๐ Analyzing shell scripts..."
+ find . -name "*.sh" -not -path "./node_modules/*" -exec shellcheck -f json {} \; > audit_reports/shellcheck_report.json 2>/dev/null || echo "[]" > audit_reports/shellcheck_report.json
+
+ # 9. Docker security analysis
+ echo "๐ณ Analyzing Docker security..."
+ if [ -f "Dockerfile" ] || [ -f "docker-compose.yml" ]; then
+ echo "Docker files found for analysis" > audit_reports/docker_analysis.txt
+ # Add basic Docker security checks
+ grep -r "ADD\|COPY\|RUN.*sudo\|USER.*root" Dockerfile* docker-compose.yml 2>/dev/null >> audit_reports/docker_analysis.txt || true
+ else
+ echo "No Docker files found" > audit_reports/docker_analysis.txt
+ fi
+
+ # 10. Environment and secrets analysis
+ echo "๐ Analyzing environment files and potential secrets..."
+ find . -name ".env*" -o -name "*.env" | head -10 > audit_reports/env_files.txt
+ # Look for potential hardcoded secrets (basic patterns)
+ grep -r -i "password\|secret\|key\|token\|api_key" --include="*.js" --include="*.ts" --include="*.py" --include="*.json" . 2>/dev/null | head -50 > audit_reports/potential_secrets.txt || touch audit_reports/potential_secrets.txt
+
+ echo "โ
Security and code analysis completed!"
+
+ - name: Generate Security Audit Report with Claude AI
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ AWS_BEDROCK_MODEL_ID: ${{ secrets.AWS_BEDROCK_MODEL_ID }}
+ INCLUDE_SECURITY_SCAN: ${{ github.event.inputs.include_security_scan }}
+ INCLUDE_DEAD_CODE_ANALYSIS: ${{ github.event.inputs.include_dead_code_analysis }}
+ INCLUDE_REFACTOR_SUGGESTIONS: ${{ github.event.inputs.include_refactor_suggestions }}
+ SEVERITY_THRESHOLD: ${{ github.event.inputs.severity_threshold }}
+ REPO_OWNER: ${{ github.repository_owner }}
+ REPO_NAME: ${{ github.event.repository.name }}
+ TARGET_BRANCH: ${{ github.event.repository.default_branch }}
+ run: |
+ cat << 'EOF' > claude_audit_generator.py
+ import boto3
+ import json
+ import os
+ import subprocess
+ import time
+ import random
+ from datetime import datetime
+ from botocore.config import Config
+ from github import Github
+ import re
+
+ class ClaudeAuditGenerator:
+ def __init__(self):
+ self.github_token = os.environ['GITHUB_TOKEN']
+ self.bedrock_client = self.get_bedrock_client()
+ self.model_id = os.environ.get('AWS_BEDROCK_MODEL_ID', 'us.anthropic.claude-3-5-sonnet-20241022-v2:0')
+ self.include_security_scan = os.environ.get('INCLUDE_SECURITY_SCAN', 'true').lower() == 'true'
+ self.include_dead_code_analysis = os.environ.get('INCLUDE_DEAD_CODE_ANALYSIS', 'true').lower() == 'true'
+ self.include_refactor_suggestions = os.environ.get('INCLUDE_REFACTOR_SUGGESTIONS', 'true').lower() == 'true'
+ self.severity_threshold = os.environ.get('SEVERITY_THRESHOLD', 'minor')
+ self.repo_owner = os.environ['REPO_OWNER']
+ self.repo_name = os.environ['REPO_NAME']
+ self.target_branch = os.environ.get('TARGET_BRANCH', 'main')
+ self.branch_name = "claude-audit"
+
+ def get_bedrock_client(self):
+ config = Config(
+ read_timeout=300,
+ connect_timeout=10,
+ retries={
+ 'max_attempts': 5,
+ 'mode': 'adaptive'
+ }
+ )
+ return boto3.client('bedrock-runtime', region_name=os.environ['AWS_DEFAULT_REGION'], config=config)
+
+ def read_file_safely(self, filepath, max_lines=500):
+ """Read file content safely, limiting lines for context"""
+ try:
+ with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
+ lines = f.readlines()
+ if len(lines) > max_lines:
+ return ''.join(lines[:max_lines]) + f'\n... (truncated, {len(lines)-max_lines} more lines)'
+ return ''.join(lines)
+ except Exception as e:
+ return f"Error reading file: {str(e)}"
+
+ def load_json_safely(self, filepath):
+ """Load JSON file safely"""
+ try:
+ with open(filepath, 'r', encoding='utf-8') as f:
+ return json.load(f)
+ except Exception as e:
+ print(f"Warning: Could not load {filepath}: {e}")
+ return {}
+
+ def analyze_audit_data(self):
+ """Analyze all security and code quality audit data"""
+ print("๐ Analyzing audit data...")
+
+ audit_data = {
+ 'npm_audit': {},
+ 'retire_scan': {},
+ 'bandit_report': {},
+ 'safety_report': {},
+ 'semgrep_report': {},
+ 'eslint_report': [],
+ 'depcheck_report': {},
+ 'shellcheck_report': [],
+ 'code_stats': {},
+ 'file_list': [],
+ 'docker_analysis': '',
+ 'potential_secrets': '',
+ 'env_files': []
+ }
+
+ # Load all audit reports
+ if os.path.exists('audit_reports'):
+ # Load JSON reports
+ json_reports = [
+ 'npm_audit', 'retire_scan', 'bandit_report', 'safety_report',
+ 'semgrep_report', 'eslint_report', 'depcheck_report',
+ 'shellcheck_report', 'code_stats'
+ ]
+
+ for report in json_reports:
+ filepath = f'audit_reports/{report}.json'
+ if os.path.exists(filepath):
+ audit_data[report] = self.load_json_safely(filepath)
+
+ # Load text reports
+ if os.path.exists('audit_reports/all_files.txt'):
+ with open('audit_reports/all_files.txt', 'r') as f:
+ audit_data['file_list'] = [line.strip() for line in f.readlines()]
+
+ if os.path.exists('audit_reports/docker_analysis.txt'):
+ audit_data['docker_analysis'] = self.read_file_safely('audit_reports/docker_analysis.txt', 100)
+
+ if os.path.exists('audit_reports/potential_secrets.txt'):
+ audit_data['potential_secrets'] = self.read_file_safely('audit_reports/potential_secrets.txt', 200)
+
+ if os.path.exists('audit_reports/env_files.txt'):
+ with open('audit_reports/env_files.txt', 'r') as f:
+ audit_data['env_files'] = [line.strip() for line in f.readlines() if line.strip()]
+
+ # Sample some source files for context
+ source_files = {}
+ key_patterns = [
+ r'src/.*\.(tsx?|jsx?)$',
+ r'.*\.(py)$',
+ r'.*\.(sql)$',
+ r'.*\.env.*$',
+ r'Dockerfile.*$',
+ r'docker-compose\.ya?ml$'
+ ]
+
+ sampled_files = []
+ for pattern in key_patterns:
+ for file_path in audit_data['file_list']:
+ if re.search(pattern, file_path, re.IGNORECASE) and len(sampled_files) < 20:
+ sampled_files.append(file_path)
+
+ # Read content of sampled files
+ for file_path in sampled_files[:15]: # Limit to avoid token overflow
+ if os.path.exists(file_path):
+ source_files[file_path] = self.read_file_safely(file_path, 50)
+
+ audit_data['source_files'] = source_files
+
+ print(f"โ
Analyzed {len(audit_data['file_list'])} files and sampled {len(sampled_files)} for review")
+ return audit_data
+
+ def generate_audit_report_with_claude(self, audit_data):
+ """Generate comprehensive security audit report using Claude"""
+
+ # Prepare comprehensive audit context
+ npm_vulns = len(audit_data.get('npm_audit', {}).get('vulnerabilities', {}))
+ retire_vulns = len(audit_data.get('retire_scan', []))
+ bandit_issues = len(audit_data.get('bandit_report', {}).get('results', []))
+ semgrep_findings = len(audit_data.get('semgrep_report', {}).get('results', []))
+ eslint_issues = len(audit_data.get('eslint_report', []))
+
+ audit_prompt = f"""
+ You are a senior security engineer and code quality expert. You need to create a comprehensive security and code quality audit report for a software project based on the provided analysis data.
+
+ ## PROJECT AUDIT DATA
+
+ **Repository:** {self.repo_owner}/{self.repo_name}
+ **Audit Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ **Severity Threshold:** {self.severity_threshold}
+
+ ### Vulnerability Summary
+ - NPM Vulnerabilities: {npm_vulns}
+ - Retired/Outdated Dependencies: {retire_vulns}
+ - Python Security Issues (Bandit): {bandit_issues}
+ - Static Analysis Findings (Semgrep): {semgrep_findings}
+ - ESLint Issues: {eslint_issues}
+
+ ### Code Statistics
+ {json.dumps(audit_data.get('code_stats', {}), indent=2)}
+
+ ### NPM Audit Results
+ {json.dumps(audit_data.get('npm_audit', {}), indent=2)[:3000]}
+
+ ### Retire.js Scan Results
+ {json.dumps(audit_data.get('retire_scan', []), indent=2)[:2000]}
+
+ ### Bandit Security Report (Python)
+ {json.dumps(audit_data.get('bandit_report', {}), indent=2)[:3000]}
+
+ ### Safety Report (Python Dependencies)
+ {json.dumps(audit_data.get('safety_report', {}), indent=2)[:2000]}
+
+ ### Semgrep Static Analysis
+ {json.dumps(audit_data.get('semgrep_report', {}), indent=2)[:4000]}
+
+ ### ESLint Report
+ {json.dumps(audit_data.get('eslint_report', []), indent=2)[:3000]}
+
+ ### Dead Code Analysis (depcheck)
+ {json.dumps(audit_data.get('depcheck_report', {}), indent=2)[:2000]}
+
+ ### ShellCheck Report
+ {json.dumps(audit_data.get('shellcheck_report', []), indent=2)[:1500]}
+
+ ### Docker Security Analysis
+ {audit_data.get('docker_analysis', 'No Docker analysis available')[:1000]}
+
+ ### Potential Secrets/Hardcoded Credentials
+ {audit_data.get('potential_secrets', 'No potential secrets found')[:2000]}
+
+ ### Environment Files
+ {audit_data.get('env_files', [])}
+
+ ### Sample Source Files
+ """
+
+ # Add sample source files (limited to avoid token overflow)
+ for file_path, content in list(audit_data.get('source_files', {}).items())[:8]:
+ audit_prompt += f"\n**{file_path}:**\n```\n{content[:800]}\n```\n"
+
+ audit_prompt += f"""
+
+ ## AUDIT REPORT GENERATION INSTRUCTIONS
+
+ Based on the provided security and code quality analysis data, create a comprehensive audit report. The report should be well-structured, actionable, and prioritized by severity.
+
+ **REQUIRED SECTIONS:**
+
+ # ๐ Security & Code Quality Audit Report
+
+ **Repository:** {self.repo_owner}/{self.repo_name}
+ **Audit Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ **Scope:** Comprehensive security and code quality analysis
+
+ ## ๐ Executive Summary
+
+ [Provide a high-level overview of findings, total issues by severity, and key recommendations]
+
+ ### Risk Assessment
+ - **Critical Issues:** [Count and brief description]
+ - **Major Issues:** [Count and brief description]
+ - **Minor Issues:** [Count and brief description]
+ - **Overall Risk Level:** [Low/Medium/High/Critical]
+
+ ## ๐จ Critical Security Issues
+
+ [List all critical severity issues that need immediate attention]
+
+ ### 1. [Issue Title]
+ - **Severity:** Critical
+ - **Category:** [Security/Code Quality/Performance]
+ - **Description:** [Detailed description]
+ - **Impact:** [Potential impact]
+ - **Location:** [File/line references]
+ - **Remediation:** [Specific steps to fix]
+
+ ## โ ๏ธ Major Issues
+
+ [List major severity issues]
+
+ ### 1. [Issue Title]
+ - **Severity:** Major
+ - **Category:** [Category]
+ - **Description:** [Description]
+ - **Impact:** [Impact]
+ - **Location:** [Location]
+ - **Remediation:** [Remediation steps]
+
+ ## ๐ Minor Issues & Improvements
+
+ [List minor issues and code quality improvements]
+
+ ## ๐ Dead Code Analysis
+
+ ### Unused Dependencies
+ [List unused npm packages that can be removed]
+
+ ### Unused Code
+ [Identify potentially unused functions, variables, or files]
+
+ ### Unused Imports
+ [List unused imports that should be cleaned up]
+
+ ## ๐ Refactoring Suggestions
+
+ ### Code Quality Improvements
+ [Suggest specific refactoring opportunities]
+
+ ### Performance Optimizations
+ [Identify performance improvement opportunities]
+
+ ### Architecture Improvements
+ [Suggest architectural or design pattern improvements]
+
+ ## ๐ก๏ธ Security Recommendations
+
+ ### Vulnerability Remediation
+ [Prioritized list of security fixes]
+
+ ### Security Best Practices
+ [General security improvements and hardening]
+
+ ### Dependency Management
+ [Recommendations for dependency updates and management]
+
+ ## ๐ง Development Workflow Improvements
+
+ ### Static Analysis Integration
+ [Recommend tools and CI/CD integration]
+
+ ### Security Testing
+ [Recommend security testing practices]
+
+ ### Code Quality Gates
+ [Suggest quality gates and standards]
+
+ ## ๐ Action Items
+
+ ### Immediate Actions (Next 1-2 weeks)
+ 1. [High priority action items]
+
+ ### Short-term Actions (Next month)
+ 1. [Medium priority improvements]
+
+ ### Long-term Actions (Next quarter)
+ 1. [Strategic improvements]
+
+ ## ๐ Metrics & Tracking
+
+ ### Current Status
+ - **Total Issues:** [Number]
+ - **Critical:** [Number]
+ - **Major:** [Number]
+ - **Minor:** [Number]
+
+ ### Progress Tracking
+ [Suggestions for tracking improvement progress]
+
+ ## ๐ Resources & References
+
+ [Include links to security guides, best practices, and tools]
+
+ ---
+
+ ## IMPORTANT INSTRUCTIONS:
+
+ 1. **Be Specific**: Provide exact file locations, line numbers when possible
+ 2. **Actionable**: Every issue should have clear remediation steps
+ 3. **Prioritized**: Order issues by severity and impact
+ 4. **Evidence-Based**: Reference actual findings from the analysis tools
+ 5. **Comprehensive**: Cover security, code quality, performance, and maintainability
+ 6. **Professional**: Use clear, professional language suitable for technical teams
+ 7. **Practical**: Focus on realistic, implementable recommendations
+
+ The report should be comprehensive enough that a development team can immediately act on the findings to improve their codebase security and quality.
+ """
+
+ return self.generate_with_retry(audit_prompt)
+
+ def generate_with_retry(self, prompt, max_retries=10):
+ """Generate audit report with retry logic"""
+ print("๐ค Generating Security Audit Report with Claude AI...")
+ print(f"๐ Prompt length: {len(prompt):,} characters")
+ print(f"๐ฏ Using model: {self.model_id}")
+ print("=" * 80)
+
+ for attempt in range(max_retries):
+ try:
+ print(f"๐ Attempt {attempt + 1}/{max_retries}")
+
+ body = {
+ "anthropic_version": "bedrock-2023-05-31",
+ "max_tokens": 20000,
+ "messages": [
+ {
+ "role": "user",
+ "content": prompt
+ }
+ ]
+ }
+
+ start_time = time.time()
+ response = self.bedrock_client.invoke_model(
+ body=json.dumps(body),
+ modelId=self.model_id,
+ accept='application/json',
+ contentType='application/json'
+ )
+
+ response_body = json.loads(response.get('body').read())
+ audit_content = response_body['content'][0]['text']
+
+ end_time = time.time()
+ generation_time = end_time - start_time
+
+ print(f"โ
Audit report generated in {generation_time:.2f} seconds!")
+ print(f"๐ Content length: {len(audit_content):,} characters")
+ print("=" * 80)
+
+ return audit_content
+
+ except Exception as e:
+ error_str = str(e)
+ print(f"โ Attempt {attempt + 1} failed: {error_str}")
+
+ if attempt < max_retries - 1:
+ delay = min(2 ** attempt + random.uniform(0, 1), 60)
+ print(f"โณ Waiting {delay:.2f} seconds before retry...")
+ time.sleep(delay)
+ else:
+ print("โ All retry attempts exhausted")
+ return None
+
+ return None
+
+ def save_audit_report(self, audit_content):
+ """Save audit report to the appropriate location"""
+ print("๐พ Saving Security Audit Report...")
+
+ # Create .github/claude directory if it doesn't exist
+ os.makedirs('.github/claude', exist_ok=True)
+
+ # Save the audit report
+ audit_file_path = '.github/claude/security-audit-report.md'
+ with open(audit_file_path, 'w', encoding='utf-8') as f:
+ f.write(audit_content)
+
+ print(f"โ
Security Audit Report saved to {audit_file_path}")
+ return audit_file_path
+
+ def create_branch_and_commit(self, audit_file_path):
+ """Create branch and commit audit report"""
+ try:
+ print(f"๐ฟ Creating branch: {self.branch_name}")
+
+ # Configure git
+ subprocess.run(['git', 'config', 'user.name', 'Claude Security Auditor'], check=True)
+ subprocess.run(['git', 'config', 'user.email', 'claude-audit@github-actions.bot'], check=True)
+
+ # Create and checkout new branch
+ subprocess.run(['git', 'checkout', '-b', self.branch_name], check=True)
+
+ # Add the audit report
+ subprocess.run(['git', 'add', audit_file_path], check=True)
+
+ # Create commit message
+ commit_msg = f"""๐ Generate comprehensive Security & Code Quality Audit Report
+
+ Auto-generated security audit by Claude AI
+
+ Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ Report Location: {audit_file_path}
+
+ This audit includes:
+ ๐จ Critical security vulnerabilities
+ โ ๏ธ Major code quality issues
+ ๐ Minor improvements and optimizations
+ ๐ Dead code and unused dependencies
+ ๐ Refactoring suggestions
+ ๐ก๏ธ Security recommendations
+ """
+
+ # Commit changes
+ subprocess.run(['git', 'commit', '-m', commit_msg], check=True)
+
+ # Push branch
+ subprocess.run(['git', 'push', '-u', 'origin', self.branch_name], check=True)
+
+ print(f"โ
Successfully pushed audit report to branch: {self.branch_name}")
+ return True
+
+ except subprocess.CalledProcessError as e:
+ print(f"โ Git operation failed: {e}")
+ return False
+
+ def create_pull_request(self, audit_file_path):
+ """Create pull request for audit report"""
+ try:
+ print("๐ Creating pull request...")
+
+ github_client = Github(self.github_token)
+
+ # Create PR in the current repository only
+ target_repo_obj = github_client.get_repo(f"{self.repo_owner}/{self.repo_name}")
+ head_ref = self.branch_name
+ base_ref = self.target_branch
+ print(f"๐ฆ Creating PR: {head_ref} -> {base_ref}")
+
+ pr_title = f"๐ Security & Code Quality Audit Report - {datetime.now().strftime('%Y-%m-%d')}"
+
+ pr_body = f"""# ๐ Security & Code Quality Audit Report
+
+ This pull request contains a comprehensive security and code quality audit report automatically generated by Claude AI.
+
+ ## ๐ Audit Details
+ - **Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ - **Location:** `{audit_file_path}`
+ - **Scope:** Complete security and code quality analysis
+ - **Severity Threshold:** {self.severity_threshold}
+
+ ## ๐ Analysis Scope
+
+ โ
**Security Vulnerabilities** - Comprehensive security scanning
+ โ
**Dependency Vulnerabilities** - NPM/Python package security
+ โ
**Static Code Analysis** - Code pattern and security analysis
+ โ
**Dead Code Detection** - Unused code and dependencies
+ โ
**Code Quality Issues** - ESLint, style, and best practices
+ โ
**Refactoring Opportunities** - Performance and maintainability
+ โ
**Docker Security** - Container configuration analysis
+ โ
**Secrets Detection** - Hardcoded credentials and sensitive data
+
+ ## ๐จ Key Findings Summary
+
+ The audit report includes categorized findings by severity:
+ - **Critical Issues:** Immediate security vulnerabilities requiring urgent attention
+ - **Major Issues:** Significant code quality and security concerns
+ - **Minor Issues:** Optimization opportunities and best practice improvements
+
+ ## ๐ Benefits
+
+ - **Enhanced Security:** Identify and remediate vulnerabilities
+ - **Improved Code Quality:** Better maintainability and readability
+ - **Performance Optimization:** Remove dead code and improve efficiency
+ - **Technical Debt Reduction:** Address anti-patterns and refactoring needs
+ - **Compliance:** Meet security and coding standards
+
+ ## ๐ Next Steps
+
+ 1. **Review** the detailed audit findings
+ 2. **Prioritize** critical and major issues for immediate action
+ 3. **Create** action items and assign to team members
+ 4. **Track** progress using the provided metrics
+ 5. **Implement** recommended security and quality improvements
+
+ ## โ ๏ธ Important Notes
+
+ - This is an automated analysis - please validate findings manually
+ - Some issues may be false positives requiring developer judgment
+ - Prioritize security vulnerabilities for immediate remediation
+ - Consider integrating recommended tools into your CI/CD pipeline
+
+ ---
+ *Generated automatically by Claude AI Security Auditor ๐*
+ """
+
+ # Try to create PR
+ try:
+ pull_request = target_repo_obj.create_pull(
+ title=pr_title,
+ body=pr_body,
+ head=head_ref,
+ base=base_ref
+ )
+
+ # Add labels
+ try:
+ pull_request.add_to_labels('security', 'audit', 'code-quality', 'claude-generated')
+ except:
+ pass # Labels might not exist
+
+ print(f"โ
Pull request created: {pull_request.html_url}")
+ return pull_request.html_url
+ except Exception as pr_error:
+ if "not permitted" in str(pr_error) or "403" in str(pr_error):
+ print("โ ๏ธ GitHub Actions cannot create PRs directly. Using GitHub CLI fallback...")
+
+ # Try using GitHub CLI
+ try:
+ cli_args = [
+ 'gh', 'pr', 'create',
+ '--title', pr_title,
+ '--body', pr_body,
+ '--head', head_ref,
+ '--base', base_ref
+ ]
+
+ result = subprocess.run(cli_args, capture_output=True, text=True, check=True)
+
+ pr_url = result.stdout.strip()
+ print(f"โ
Pull request created via GitHub CLI: {pr_url}")
+ return pr_url
+ except subprocess.CalledProcessError as cli_error:
+ print(f"โ GitHub CLI also failed: {cli_error}")
+ return None
+ else:
+ raise pr_error
+
+ except Exception as e:
+ print(f"โ Failed to create pull request: {e}")
+ return None
+
+ def run_audit(self):
+ """Main audit execution method"""
+ print("๐ Starting Claude AI Security & Code Quality Audit...")
+ print(f"๐ Security scan: {self.include_security_scan}")
+ print(f"๐ Dead code analysis: {self.include_dead_code_analysis}")
+ print(f"๐ Refactor suggestions: {self.include_refactor_suggestions}")
+ print(f"โ ๏ธ Severity threshold: {self.severity_threshold}")
+ print("=" * 80)
+
+ # Step 1: Analyze audit data
+ audit_data = self.analyze_audit_data()
+
+ # Step 2: Generate audit report with Claude
+ audit_content = self.generate_audit_report_with_claude(audit_data)
+
+ if not audit_content:
+ print("โ Failed to generate audit report")
+ return False
+
+ # Step 3: Save audit report
+ audit_file_path = self.save_audit_report(audit_content)
+
+ # Step 4: Create branch and commit
+ if self.create_branch_and_commit(audit_file_path):
+ # Step 5: Create pull request
+ pr_url = self.create_pull_request(audit_file_path)
+
+ if pr_url:
+ print("=" * 80)
+ print("๐ Security audit completed successfully!")
+ print(f"๐ Audit Report: {audit_file_path}")
+ print(f"๐ Pull Request: {pr_url}")
+ print("=" * 80)
+ return True
+ else:
+ print("=" * 80)
+ print("๐ Security audit completed successfully!")
+ print(f"๐ Audit Report: {audit_file_path}")
+ print(f"๐ฟ Branch: {self.branch_name}")
+ print("๐ก Create a pull request manually from the GitHub interface")
+ print("=" * 80)
+ return True
+ else:
+ print("โ Failed to create branch and commit audit report")
+ return False
+
+ if __name__ == "__main__":
+ auditor = ClaudeAuditGenerator()
+ success = auditor.run_audit()
+ exit(0 if success else 1)
+ EOF
+
+ python claude_audit_generator.py
+
+ - name: Summary
+ if: always()
+ run: |
+ echo "๐ Claude AI Security & Code Quality Audit Summary"
+ echo "================================================="
+ echo "Security Scan: ${{ github.event.inputs.include_security_scan }}"
+ echo "Dead Code Analysis: ${{ github.event.inputs.include_dead_code_analysis }}"
+ echo "Refactor Suggestions: ${{ github.event.inputs.include_refactor_suggestions }}"
+ echo "Severity Threshold: ${{ github.event.inputs.severity_threshold }}"
+ echo "Branch: claude-audit"
+ echo "Report Location: .github/claude/security-audit-report.md"
+ echo ""
+ echo "๐ The audit report includes:"
+ echo " ๐จ Critical security vulnerabilities"
+ echo " โ ๏ธ Major code quality issues"
+ echo " ๐ Minor improvements and optimizations"
+ echo " ๐ Dead code and unused dependencies"
+ echo " ๐ Refactoring suggestions"
+ echo " ๐ก๏ธ Security recommendations"
+ echo " ๐ Prioritized action items"
+ echo ""
+ echo "๐ Check the pull request for the comprehensive security audit report!"
diff --git a/.github/workflows/claude-generate.yml b/.github/workflows/claude-generate.yml
new file mode 100644
index 0000000..19908a5
--- /dev/null
+++ b/.github/workflows/claude-generate.yml
@@ -0,0 +1,328 @@
+name: Claude AI Code Generation
+
+on:
+ issues:
+ types: [opened, edited]
+ issue_comment:
+ types: [created]
+ workflow_dispatch:
+ inputs:
+ task_description:
+ description: "Describe the code generation task"
+ required: true
+ type: string
+ target_branch:
+ description: "Target branch for the generated code"
+ required: false
+ default: "claude-generated"
+ type: string
+
+jobs:
+ generate-code:
+ runs-on: ubuntu-latest
+ if: |
+ (github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'claude-generate')) ||
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '/claude generate')) ||
+ (github.event_name == 'workflow_dispatch')
+
+ permissions:
+ contents: write
+ pull-requests: write
+ issues: write
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ${{ secrets.AWS_REGION }}
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "18"
+ cache: "npm"
+
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.11"
+
+ - name: Install dependencies
+ run: |
+ npm install
+ pip install boto3 requests botocore
+
+ - name: Extract task description
+ id: task
+ run: |
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
+ echo "description=${{ github.event.inputs.task_description }}" >> $GITHUB_OUTPUT
+ echo "branch=${{ github.event.inputs.target_branch }}" >> $GITHUB_OUTPUT
+ elif [ "${{ github.event_name }}" = "issues" ]; then
+ # Extract task from issue body
+ echo "description<> $GITHUB_OUTPUT
+ echo "${{ github.event.issue.body }}" >> $GITHUB_OUTPUT
+ echo "EOF" >> $GITHUB_OUTPUT
+ echo "branch=claude-generated-issue-${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
+ else
+ # Extract task from comment
+ COMMENT_BODY="${{ github.event.comment.body }}"
+ TASK_DESC=$(echo "$COMMENT_BODY" | sed -n 's|^/claude generate \(.*\)|\1|p')
+ echo "description=$TASK_DESC" >> $GITHUB_OUTPUT
+ echo "branch=claude-generated-comment-${{ github.event.comment.id }}" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Analyze codebase structure
+ run: |
+ find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) \
+ -not -path "./node_modules/*" \
+ -not -path "./.git/*" > codebase_files.txt
+
+ echo "Project structure:" > project_context.txt
+ tree -I 'node_modules|.git' -L 3 >> project_context.txt || ls -la >> project_context.txt
+
+ echo "Package.json content:" >> project_context.txt
+ cat package.json >> project_context.txt
+
+ - name: Generate code with Claude
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ AWS_BEDROCK_MODEL_ID: ${{ secrets.AWS_BEDROCK_MODEL_ID }}
+ TASK_DESCRIPTION: ${{ steps.task.outputs.description }}
+ TARGET_BRANCH: ${{ steps.task.outputs.branch }}
+ run: |
+ cat << 'EOF' > claude_generator.py
+ import boto3
+ import json
+ import os
+ import requests
+ import subprocess
+ import tempfile
+ from botocore.config import Config
+
+
+ def get_bedrock_client():
+ config = Config(
+ read_timeout=120, # Increase from default (~60s)
+ connect_timeout=10, # Optional, you can tweak this too
+ retries={
+ 'max_attempts': 3,
+ 'mode': 'standard'
+ }
+ )
+ return boto3.client('bedrock-runtime', region_name=os.environ['AWS_DEFAULT_REGION'], config=config)
+
+ def read_file_safely(filepath, max_lines=100):
+ """Read file content safely, limiting lines for context"""
+ try:
+ with open(filepath, 'r', encoding='utf-8') as f:
+ lines = f.readlines()
+ if len(lines) > max_lines:
+ return ''.join(lines[:max_lines]) + f'\n... (truncated, {len(lines)-max_lines} more lines)'
+ return ''.join(lines)
+ except Exception as e:
+ return f"Error reading file: {str(e)}"
+
+ def analyze_codebase():
+ """Analyze the current codebase structure"""
+ context = ""
+
+ # Read project context
+ if os.path.exists('project_context.txt'):
+ context += read_file_safely('project_context.txt')
+
+ # Read key configuration files
+ key_files = ['package.json', 'tsconfig.json', 'vite.config.ts', 'capacitor.config.ts']
+ for file in key_files:
+ if os.path.exists(file):
+ context += f"\n\n=== {file} ===\n"
+ context += read_file_safely(file, 50)
+
+ # Sample some source files to understand patterns
+ if os.path.exists('codebase_files.txt'):
+ with open('codebase_files.txt', 'r') as f:
+ files = [line.strip() for line in f.readlines()[:10]] # First 10 files
+
+ for file in files:
+ if os.path.exists(file):
+ context += f"\n\n=== {file} ===\n"
+ context += read_file_safely(file, 30)
+
+ return context
+
+ def generate_code_with_claude(task_description, codebase_context):
+ client = get_bedrock_client()
+
+ prompt = f"""
+ You are an expert software developer working on an Ionic React TypeScript application for government billing/invoicing.
+
+ TASK: {task_description}
+
+ CURRENT CODEBASE CONTEXT:
+ {codebase_context}
+
+ Please generate the necessary code changes to implement the requested feature. Your response should include:
+
+ 1. **FILES_TO_CREATE**: List any new files that need to be created with their full paths
+ 2. **FILES_TO_MODIFY**: List any existing files that need to be modified
+ 3. **CODE_CHANGES**: Provide the actual code for new files or specific changes for existing files
+ 4. **INSTRUCTIONS**: Any additional setup or configuration steps needed
+
+ Follow these guidelines:
+ - Use TypeScript and React hooks
+ - Follow Ionic React patterns and components
+ - Maintain consistency with existing code style
+ - Include proper error handling
+ - Add appropriate TypeScript types
+ - Use existing utilities and patterns from the codebase
+ - Ensure mobile-first responsive design
+
+ Format your response clearly with sections for each file change.
+ """
+
+ body = {
+ "anthropic_version": "bedrock-2023-05-31",
+ "max_tokens": 8000,
+ "messages": [
+ {
+ "role": "user",
+ "content": prompt
+ }
+ ]
+ }
+
+ response = client.invoke_model(
+ body=json.dumps(body),
+ modelId=os.environ.get('AWS_BEDROCK_MODEL_ID', 'anthropic.claude-3-sonnet-20240229-v1:0'),
+ accept='application/json',
+ contentType='application/json'
+ )
+
+ response_body = json.loads(response.get('body').read())
+ return response_body['content'][0]['text']
+
+ def create_branch_and_commit(branch_name, generated_content):
+ """Create a new branch and commit the generated code"""
+ try:
+
+ subprocess.run(['git', 'config', 'user.name', 'Claude Bot'], check=True)
+ subprocess.run(['git', 'config', 'user.email', 'claude-bot@example.com'], check=True)
+
+ # Create and checkout new branch
+ subprocess.run(['git', 'checkout', '-b', branch_name], check=True)
+
+ # Create a summary file with the generated content
+ with open('CLAUDE_GENERATED.md', 'w') as f:
+ f.write(f"# Claude Generated Code\n\n")
+ f.write(f"**Task**: {os.environ['TASK_DESCRIPTION']}\n\n")
+ f.write(f"**Generated on**: {subprocess.check_output(['date']).decode().strip()}\n\n")
+ f.write("## Generated Content\n\n")
+ f.write("```\n")
+ f.write(generated_content)
+ f.write("\n```\n")
+
+ # Stage and commit changes
+ subprocess.run(['git', 'add', '.'], check=True)
+ subprocess.run(['git', 'commit', '-m', f'Add Claude generated code\n\nTask: {os.environ["TASK_DESCRIPTION"]}\n\nGenerated by Claude AI via Amazon Bedrock'], check=True)
+
+ # Push branch
+ subprocess.run(['git', 'push', '-u', 'origin', branch_name], check=True)
+
+ return True
+ except subprocess.CalledProcessError as e:
+ print(f"Git operation failed: {e}")
+ return False
+
+ def create_pull_request(branch_name, task_description, generated_content):
+ """Create a pull request with the generated code"""
+ github_token = os.environ['GITHUB_TOKEN']
+ repo = os.environ['GITHUB_REPOSITORY']
+
+ headers = {
+ 'Authorization': f'token {github_token}',
+ 'Accept': 'application/vnd.github.v3+json'
+ }
+
+ pr_body = f"""## ๐ค Claude AI Generated Code
+
+ **Task Description:** {task_description}
+
+ This pull request contains code generated by Claude AI via Amazon Bedrock based on the requested feature.
+
+ ## Generated Changes
+
+ ```
+ {generated_content[:2000]}{'...' if len(generated_content) > 2000 else ''}
+ ```
+
+ ## Review Notes
+
+ - Please review the generated code carefully
+ - Test the functionality before merging
+ - Make any necessary adjustments for your specific requirements
+ - Check for integration with existing codebase
+
+ ---
+ *This PR was created automatically by Claude AI*
+ """
+
+ pr_data = {
+ 'title': f'๐ค Claude Generated: {task_description[:50]}{"..." if len(task_description) > 50 else ""}',
+ 'head': branch_name,
+ 'base': 'main',
+ 'body': pr_body
+ }
+
+ url = f'https://api.github.com/repos/{repo}/pulls'
+ response = requests.post(url, headers=headers, json=pr_data)
+
+ if response.status_code == 201:
+ pr_url = response.json()['html_url']
+ print(f"โ
Pull request created: {pr_url}")
+ return pr_url
+ else:
+ print(f"โ Failed to create PR: {response.status_code}")
+ print(response.text)
+ return None
+
+ def main():
+ task_description = os.environ['TASK_DESCRIPTION']
+ branch_name = os.environ['TARGET_BRANCH']
+
+ print(f"๐ Generating code for task: {task_description}")
+
+ # Analyze codebase
+ print("๐ Analyzing codebase...")
+ codebase_context = analyze_codebase()
+
+ # Generate code with Claude
+ print("๐ง Generating code with Claude...")
+ generated_content = generate_code_with_claude(task_description, codebase_context)
+
+ # Create branch and commit
+ print(f"๐ฟ Creating branch: {branch_name}")
+ if create_branch_and_commit(branch_name, generated_content):
+ # Create pull request
+ print("๐ Creating pull request...")
+ pr_url = create_pull_request(branch_name, task_description, generated_content)
+
+ if pr_url:
+ print(f"๐ Code generation completed successfully!")
+ print(f"Pull request: {pr_url}")
+ else:
+ print("โ ๏ธ Code generated but PR creation failed")
+ else:
+ print("โ Failed to create branch and commit changes")
+
+ if __name__ == "__main__":
+ main()
+ EOF
+
+ python claude_generator.py
diff --git a/.github/workflows/claude-organize.yml b/.github/workflows/claude-organize.yml
new file mode 100644
index 0000000..735902f
--- /dev/null
+++ b/.github/workflows/claude-organize.yml
@@ -0,0 +1,139 @@
+name: Claude AI Comment Organizer
+
+on:
+ issue_comment:
+ types: [created]
+
+jobs:
+ claude-organize:
+ runs-on: ubuntu-latest
+ # Only run if the comment contains "/claude-organize"
+ if: contains(github.event.comment.body, '/claude-organize')
+ permissions:
+ contents: read
+ issues: write
+ pull-requests: write
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ${{ secrets.AWS_REGION }}
+
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.11"
+
+ - name: Install dependencies
+ run: |
+ pip install boto3 requests
+
+ - name: Organize comment with Claude
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ AWS_BEDROCK_MODEL_ID: ${{ secrets.AWS_BEDROCK_MODEL_ID }}
+ COMMENT_BODY: ${{ github.event.comment.body }}
+ COMMENT_ID: ${{ github.event.comment.id }}
+ COMMENT_URL: ${{ github.event.comment.html_url }}
+ run: |
+ cat << 'EOF' > claude_organize.py
+ import boto3
+ import json
+ import os
+ import requests
+ import re
+
+ def get_bedrock_client():
+ return boto3.client('bedrock-runtime', region_name=os.environ['AWS_DEFAULT_REGION'])
+
+ def organize_with_claude(comment_text):
+ client = get_bedrock_client()
+
+ # Remove the /claude-organize trigger from the comment
+ cleaned_comment = re.sub(r'/claude-organize\s*', '', comment_text, flags=re.IGNORECASE).strip()
+
+ if not cleaned_comment:
+ return "No content to organize after removing the /claude-organize command."
+
+ prompt = f"""
+ You are an expert at organizing and structuring text content. Please take the following comment and reorganize it into a clear, well-structured format with proper bullet points and organization.
+
+ Original comment:
+ ```
+ {cleaned_comment}
+ ```
+
+ Please reorganize this content following these guidelines:
+ 1. Create clear sections with appropriate headings if the content covers multiple topics
+ 2. Use bullet points and sub-bullets for better readability
+ 3. Group related information together
+ 4. Maintain all the original information - don't add or remove content
+ 5. Use proper markdown formatting
+ 6. If there are action items, group them together
+ 7. If there are questions, group them together
+ 8. Make the structure logical and easy to follow
+
+ Return only the organized content without any explanatory text about what you did.
+ """
+
+ body = {
+ "anthropic_version": "bedrock-2023-05-31",
+ "max_tokens": 4000,
+ "messages": [
+ {
+ "role": "user",
+ "content": prompt
+ }
+ ]
+ }
+
+ response = client.invoke_model(
+ body=json.dumps(body),
+ modelId=os.environ.get('AWS_BEDROCK_MODEL_ID', 'anthropic.claude-3-sonnet-20240229-v1:0'),
+ accept='application/json',
+ contentType='application/json'
+ )
+
+ response_body = json.loads(response.get('body').read())
+ return response_body['content'][0]['text']
+
+ def update_original_comment(organized_content, comment_id):
+ github_token = os.environ['GITHUB_TOKEN']
+ repo = os.environ['GITHUB_REPOSITORY']
+
+ headers = {
+ 'Authorization': f'token {github_token}',
+ 'Accept': 'application/vnd.github.v3+json'
+ }
+
+ # Update the original comment with the organized content only
+ url = f'https://api.github.com/repos/{repo}/issues/comments/{comment_id}'
+ response = requests.patch(url, headers=headers, json={'body': organized_content})
+
+ if response.status_code == 200:
+ print("โ
Original comment updated successfully")
+ else:
+ print(f"โ Failed to update comment: {response.status_code}")
+ print(response.text)
+
+ def main():
+ comment_body = os.environ['COMMENT_BODY']
+ comment_id = os.environ['COMMENT_ID']
+
+ print("๐ Organizing comment with Claude...")
+ organized_content = organize_with_claude(comment_body)
+
+ print("๐ Updating original comment...")
+ update_original_comment(organized_content, comment_id)
+
+ if __name__ == "__main__":
+ main()
+ EOF
+
+ python claude_organize.py
diff --git a/.github/workflows/claude-pr-review.yml b/.github/workflows/claude-pr-review.yml
new file mode 100644
index 0000000..2b5f53e
--- /dev/null
+++ b/.github/workflows/claude-pr-review.yml
@@ -0,0 +1,164 @@
+name: Claude AI PR Review
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened]
+ pull_request_review_comment:
+ types: [created]
+
+jobs:
+ claude-review:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+ issues: write
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Get PR changes
+ id: changes
+ run: |
+ # Get the list of changed files
+ git diff --name-only origin/${{ github.base_ref }}..HEAD > changed_files.txt
+
+ # Get the diff content
+ git diff origin/${{ github.base_ref }}..HEAD > changes.diff
+
+ echo "Changed files:"
+ cat changed_files.txt
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ${{ secrets.AWS_REGION }}
+
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.11"
+
+ - name: Install dependencies
+ run: |
+ pip install boto3 requests
+
+ - name: Run Claude PR Review
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ AWS_BEDROCK_MODEL_ID: ${{ secrets.AWS_BEDROCK_MODEL_ID }}
+ run: |
+ cat << 'EOF' > claude_review.py
+ import boto3
+ import json
+ import os
+ import requests
+ import base64
+
+ def get_bedrock_client():
+ return boto3.client('bedrock-runtime', region_name=os.environ['AWS_DEFAULT_REGION'])
+
+ def analyze_with_claude(diff_content, changed_files):
+ client = get_bedrock_client()
+
+ prompt = f"""
+ You are an expert code reviewer. Please review the following code changes and provide constructive feedback.
+
+ Changed files: {', '.join(changed_files)}
+
+ Code diff:
+ ```diff
+ {diff_content}
+ ```
+
+ Please provide:
+ 1. Overall assessment of the changes
+ 2. Potential issues or bugs
+ 3. Code quality suggestions
+ 4. Security considerations
+ 5. Performance implications
+ 6. Suggestions for improvement
+
+ Focus on being constructive and helpful. If the code looks good, mention what's done well.
+ """
+
+ body = {
+ "anthropic_version": "bedrock-2023-05-31",
+ "max_tokens": 4000,
+ "messages": [
+ {
+ "role": "user",
+ "content": prompt
+ }
+ ]
+ }
+
+ response = client.invoke_model(
+ body=json.dumps(body),
+ modelId=os.environ.get('AWS_BEDROCK_MODEL_ID', 'anthropic.claude-3-sonnet-20240229-v1:0'),
+ accept='application/json',
+ contentType='application/json'
+ )
+
+ response_body = json.loads(response.get('body').read())
+ return response_body['content'][0]['text']
+
+ def post_pr_comment(review_content):
+ github_token = os.environ['GITHUB_TOKEN']
+ repo = os.environ['GITHUB_REPOSITORY']
+ pr_number = os.environ['GITHUB_PR_NUMBER'] if 'GITHUB_PR_NUMBER' in os.environ else os.environ['GITHUB_REF'].split('/')[-2]
+
+ headers = {
+ 'Authorization': f'token {github_token}',
+ 'Accept': 'application/vnd.github.v3+json'
+ }
+
+ comment_body = f"""## ๐ค Claude AI Code Review
+
+ {review_content}
+
+ ---
+ *This review was generated automatically by Claude AI via Amazon Bedrock*
+ """
+
+ url = f'https://api.github.com/repos/{repo}/issues/{pr_number}/comments'
+ response = requests.post(url, headers=headers, json={'body': comment_body})
+
+ if response.status_code == 201:
+ print("โ
Review comment posted successfully")
+ else:
+ print(f"โ Failed to post comment: {response.status_code}")
+ print(response.text)
+
+ def main():
+ # Read changed files
+ with open('changed_files.txt', 'r') as f:
+ changed_files = [line.strip() for line in f.readlines() if line.strip()]
+
+ # Read diff content
+ with open('changes.diff', 'r') as f:
+ diff_content = f.read()
+
+ if not diff_content.strip():
+ print("No changes to review")
+ return
+
+ print("๐ Analyzing changes with Claude...")
+ review = analyze_with_claude(diff_content, changed_files)
+
+ print("๐ Posting review comment...")
+ post_pr_comment(review)
+
+ if __name__ == "__main__":
+ main()
+ EOF
+
+ # Set PR number for API calls
+ export GITHUB_PR_NUMBER=${{ github.event.number }}
+
+ python claude_review.py
diff --git a/.github/workflows/claude-readme.yml b/.github/workflows/claude-readme.yml
new file mode 100644
index 0000000..cc590f6
--- /dev/null
+++ b/.github/workflows/claude-readme.yml
@@ -0,0 +1,804 @@
+name: Claude AI README Generator
+
+on:
+ workflow_dispatch:
+ inputs:
+ include_detailed_structure:
+ description: "Include detailed project structure"
+ required: false
+ default: true
+ type: boolean
+ include_tech_analysis:
+ description: "Include comprehensive tech stack analysis"
+ required: false
+ default: true
+ type: boolean
+ update_existing:
+ description: "Update existing README if it exists"
+ required: false
+ default: true
+ type: boolean
+
+jobs:
+ generate-readme:
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: write
+ pull-requests: write
+ actions: read
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Check and handle existing claude-readme branch
+ id: check-branch
+ run: |
+ if git ls-remote --heads origin claude-readme | grep -q claude-readme; then
+ echo "branch_exists=true" >> $GITHUB_OUTPUT
+ echo "โ ๏ธ Branch 'claude-readme' already exists!"
+ echo "๐๏ธ Automatically deleting existing branch..."
+ git push origin --delete claude-readme || echo "Branch deletion failed, but continuing..."
+ echo "โ
Existing branch deleted. Proceeding with README generation."
+ else
+ echo "branch_exists=false" >> $GITHUB_OUTPUT
+ echo "โ
Branch 'claude-readme' does not exist. Proceeding with README generation."
+ fi
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ${{ secrets.AWS_REGION }}
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "18"
+ cache: "npm"
+
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.11"
+
+ - name: Install dependencies
+ run: |
+ # Install Python dependencies
+ pip install boto3 requests botocore PyGithub
+
+ # Install GitHub CLI as backup for PR creation
+ type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
+ curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
+ && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
+ && sudo apt update \
+ && sudo apt install gh -y
+
+ # Install additional tools for analysis
+ sudo apt install -y tree file
+
+ - name: Analyze project structure and files
+ run: |
+ echo "๐ Analyzing project structure and files..."
+
+ # Create comprehensive file inventory
+ find . -type f \( \
+ -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o \
+ -name "*.css" -o -name "*.scss" -o -name "*.json" -o -name "*.md" -o \
+ -name "*.yml" -o -name "*.yaml" -o -name "*.toml" -o -name "*.xml" -o \
+ -name "*.html" -o -name "*.svg" -o -name "*.png" -o -name "*.jpg" -o \
+ -name "*.ico" -o -name "*.txt" -o -name "Dockerfile*" -o -name "*.sh" \
+ \) \
+ -not -path "./node_modules/*" \
+ -not -path "./.git/*" \
+ -not -path "./dist/*" \
+ -not -path "./build/*" \
+ -not -path "./coverage/*" > all_files.txt
+
+ echo "๐ Found $(wc -l < all_files.txt) files to analyze"
+
+ # Generate project tree structure
+ echo "Project Directory Structure:" > project_tree.txt
+ echo "============================" >> project_tree.txt
+ tree -I 'node_modules|.git|dist|build|coverage|.next|.vscode|.idea' -L 4 >> project_tree.txt 2>/dev/null || find . -type d -not -path "./node_modules*" -not -path "./.git*" -not -path "./dist*" -not -path "./build*" | head -50 >> project_tree.txt
+
+ # Analyze key configuration files
+ echo "Configuration Files Analysis:" > config_files.txt
+ echo "=============================" >> config_files.txt
+
+ # List of important config files to analyze
+ config_files=(
+ "package.json"
+ "tsconfig.json"
+ "vite.config.ts"
+ "vite.config.js"
+ "capacitor.config.ts"
+ "capacitor.config.js"
+ "ionic.config.json"
+ ".eslintrc.json"
+ ".eslintrc.js"
+ "webpack.config.js"
+ "docker-compose.yml"
+ "Dockerfile"
+ "README.md"
+ "LICENSE"
+ ".gitignore"
+ "pwa-assets.config.ts"
+ "manifest.json"
+ )
+
+ for file in "${config_files[@]}"; do
+ if [ -f "$file" ]; then
+ echo "=== $file ===" >> config_files.txt
+ head -50 "$file" >> config_files.txt
+ echo "" >> config_files.txt
+ fi
+ done
+
+ # Analyze source code structure
+ echo "Source Code Analysis:" > source_analysis.txt
+ echo "=====================" >> source_analysis.txt
+
+ # Count different file types
+ echo "File Type Distribution:" >> source_analysis.txt
+ echo "TypeScript files: $(find . -name "*.ts" -not -path "./node_modules/*" | wc -l)" >> source_analysis.txt
+ echo "TypeScript React files: $(find . -name "*.tsx" -not -path "./node_modules/*" | wc -l)" >> source_analysis.txt
+ echo "JavaScript files: $(find . -name "*.js" -not -path "./node_modules/*" | wc -l)" >> source_analysis.txt
+ echo "JavaScript React files: $(find . -name "*.jsx" -not -path "./node_modules/*" | wc -l)" >> source_analysis.txt
+ echo "CSS/SCSS files: $(find . -name "*.css" -o -name "*.scss" -not -path "./node_modules/*" | wc -l)" >> source_analysis.txt
+ echo "JSON files: $(find . -name "*.json" -not -path "./node_modules/*" | wc -l)" >> source_analysis.txt
+ echo "Markdown files: $(find . -name "*.md" -not -path "./node_modules/*" | wc -l)" >> source_analysis.txt
+ echo "" >> source_analysis.txt
+
+ # Analyze directory structure
+ echo "Main Directories:" >> source_analysis.txt
+ find . -maxdepth 2 -type d -not -path "./node_modules*" -not -path "./.git*" | sort >> source_analysis.txt
+
+ - name: Generate README with Claude AI
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ AWS_BEDROCK_MODEL_ID: ${{ secrets.AWS_BEDROCK_MODEL_ID }}
+ INCLUDE_DETAILED_STRUCTURE: ${{ github.event.inputs.include_detailed_structure }}
+ INCLUDE_TECH_ANALYSIS: ${{ github.event.inputs.include_tech_analysis }}
+ UPDATE_EXISTING: ${{ github.event.inputs.update_existing }}
+ REPO_OWNER: ${{ github.repository_owner }}
+ REPO_NAME: ${{ github.event.repository.name }}
+ TARGET_BRANCH: ${{ github.event.repository.default_branch }}
+ run: |
+ cat << 'EOF' > claude_readme_generator.py
+ import boto3
+ import json
+ import os
+ import subprocess
+ import time
+ import random
+ from datetime import datetime
+ from botocore.config import Config
+ from github import Github
+ import re
+
+ class ClaudeReadmeGenerator:
+ def __init__(self):
+ self.github_token = os.environ['GITHUB_TOKEN']
+ self.bedrock_client = self.get_bedrock_client()
+ self.model_id = os.environ.get('AWS_BEDROCK_MODEL_ID', 'us.anthropic.claude-3-5-sonnet-20241022-v2:0')
+ self.include_detailed_structure = os.environ.get('INCLUDE_DETAILED_STRUCTURE', 'true').lower() == 'true'
+ self.include_tech_analysis = os.environ.get('INCLUDE_TECH_ANALYSIS', 'true').lower() == 'true'
+ self.update_existing = os.environ.get('UPDATE_EXISTING', 'true').lower() == 'true'
+ self.repo_owner = os.environ['REPO_OWNER']
+ self.repo_name = os.environ['REPO_NAME']
+ self.target_branch = os.environ.get('TARGET_BRANCH', 'main')
+ self.branch_name = "claude-readme"
+
+ def get_bedrock_client(self):
+ config = Config(
+ read_timeout=300,
+ connect_timeout=10,
+ retries={
+ 'max_attempts': 5,
+ 'mode': 'adaptive'
+ }
+ )
+ return boto3.client('bedrock-runtime', region_name=os.environ['AWS_DEFAULT_REGION'], config=config)
+
+ def read_file_safely(self, filepath, max_lines=200):
+ """Read file content safely, limiting lines for context"""
+ try:
+ with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
+ lines = f.readlines()
+ if len(lines) > max_lines:
+ return ''.join(lines[:max_lines]) + f'\n... (truncated, {len(lines)-max_lines} more lines)'
+ return ''.join(lines)
+ except Exception as e:
+ return f"Error reading file: {str(e)}"
+
+ def analyze_project_context(self):
+ """Analyze the project structure and files for README generation"""
+ print("๐ Analyzing project context...")
+
+ context = {
+ 'project_tree': '',
+ 'config_files': '',
+ 'source_analysis': '',
+ 'key_files_content': {},
+ 'sample_source_files': {},
+ 'file_stats': {}
+ }
+
+ # Read project tree structure
+ if os.path.exists('project_tree.txt'):
+ context['project_tree'] = self.read_file_safely('project_tree.txt', 150)
+
+ # Read configuration files analysis
+ if os.path.exists('config_files.txt'):
+ context['config_files'] = self.read_file_safely('config_files.txt', 300)
+
+ # Read source code analysis
+ if os.path.exists('source_analysis.txt'):
+ context['source_analysis'] = self.read_file_safely('source_analysis.txt', 100)
+
+ # Read all files list
+ all_files = []
+ if os.path.exists('all_files.txt'):
+ with open('all_files.txt', 'r') as f:
+ all_files = [line.strip() for line in f.readlines() if line.strip()]
+
+ # Sample key source files for understanding project structure
+ key_patterns = [
+ r'src/App\.(tsx?|jsx?)$',
+ r'src/main\.(tsx?|jsx?)$',
+ r'src/index\.(tsx?|jsx?)$',
+ r'src/.*[Pp]age.*\.(tsx?|jsx?)$',
+ r'src/.*[Cc]omponent.*\.(tsx?|jsx?)$',
+ r'src/.*[Ss]ervice.*\.(tsx?|jsx?)$',
+ r'src/.*[Uu]til.*\.(tsx?|jsx?)$',
+ r'src/.*[Hh]ook.*\.(tsx?|jsx?)$',
+ r'src/.*[Cc]ontext.*\.(tsx?|jsx?)$',
+ ]
+
+ sampled_files = []
+ for pattern in key_patterns:
+ for file_path in all_files:
+ if re.search(pattern, file_path, re.IGNORECASE) and len(sampled_files) < 15:
+ sampled_files.append(file_path)
+
+ # Add some additional important files
+ for file_path in all_files[:10]:
+ if len(sampled_files) < 20 and file_path not in sampled_files:
+ sampled_files.append(file_path)
+
+ # Read content of sampled files
+ for file_path in sampled_files:
+ if os.path.exists(file_path):
+ context['sample_source_files'][file_path] = self.read_file_safely(file_path, 100)
+
+ # Calculate file statistics
+ context['file_stats'] = {
+ 'total_files': len(all_files),
+ 'typescript_files': len([f for f in all_files if f.endswith(('.ts', '.tsx'))]),
+ 'javascript_files': len([f for f in all_files if f.endswith(('.js', '.jsx'))]),
+ 'css_files': len([f for f in all_files if f.endswith(('.css', '.scss'))]),
+ 'config_files': len([f for f in all_files if f.endswith(('.json', '.yml', '.yaml', '.toml'))]),
+ 'image_files': len([f for f in all_files if f.endswith(('.png', '.jpg', '.jpeg', '.svg', '.ico'))]),
+ }
+
+ print(f"โ
Analyzed {len(all_files)} files and sampled {len(sampled_files)} key files")
+ return context
+
+ def generate_readme_with_claude(self, project_context):
+ """Generate comprehensive README using Claude"""
+
+ readme_prompt = f"""
+ You are an expert technical writer and software architect. You need to create a comprehensive, professional README.md file for a software project based on the provided project analysis.
+
+ ## PROJECT ANALYSIS DATA
+
+ **Repository:** {self.repo_owner}/{self.repo_name}
+ **Analysis Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+
+ ### File Statistics
+ - Total files: {project_context['file_stats']['total_files']}
+ - TypeScript files: {project_context['file_stats']['typescript_files']}
+ - JavaScript files: {project_context['file_stats']['javascript_files']}
+ - CSS/Style files: {project_context['file_stats']['css_files']}
+ - Configuration files: {project_context['file_stats']['config_files']}
+ - Image/Asset files: {project_context['file_stats']['image_files']}
+
+ ### Project Directory Structure
+ ```
+ {project_context['project_tree']}
+ ```
+
+ ### Configuration Files Analysis
+ {project_context['config_files']}
+
+ ### Source Code Analysis
+ {project_context['source_analysis']}
+
+ ### Sample Source Files
+ """
+
+ # Add sample source files
+ for file_path, content in project_context['sample_source_files'].items():
+ readme_prompt += f"\n**{file_path}:**\n```\n{content[:1000]}\n```\n"
+
+ readme_prompt += """
+
+ ## README GENERATION INSTRUCTIONS
+
+ Based on the provided project analysis, create a comprehensive and professional README.md file. The README should be well-structured, informative, and follow modern documentation standards.
+
+ **REQUIRED SECTIONS:**
+
+ # [Project Name]
+
+ [Brief, compelling project description - 2-3 sentences explaining what this project does]
+
+ ## ๐ Features
+
+ [List key features and capabilities based on code analysis]
+
+ ## ๐ ๏ธ Tech Stack
+
+ [Comprehensive technology stack analysis based on configuration files and dependencies]
+
+ ### Frontend
+ - [List frontend technologies]
+
+ ### Backend/Services
+ - [List backend technologies if any]
+
+ ### Development Tools
+ - [List development and build tools]
+
+ ### Mobile/Desktop
+ - [List mobile/desktop technologies if applicable]
+
+ ## ๐ Project Structure
+
+ [Explain the main directories and their purposes]
+
+ ## ๐ง Installation & Setup
+
+ ### Prerequisites
+ [List required software/tools]
+
+ ### Installation Steps
+ ```bash
+ # Step-by-step installation commands
+ ```
+
+ ## ๐ฏ Usage
+
+ ### Development
+ ```bash
+ # Commands to run in development
+ ```
+
+ ### Production
+ ```bash
+ # Commands to build and run in production
+ ```
+
+ ### Mobile Development
+ [If applicable, include mobile-specific instructions]
+
+ ## ๐ฑ Platform Support
+
+ [List supported platforms based on configuration]
+
+ ## ๐งช Testing
+
+ [Include testing instructions if test files are found]
+
+ ## ๐ Deployment
+
+ [Include deployment information based on configuration files]
+
+ ## ๐ Performance & Optimization
+
+ [Include performance considerations if applicable]
+
+ ## ๐ค Contributing
+
+ We welcome contributions! Please follow these guidelines:
+
+ 1. Fork the repository
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
+ 5. Open a Pull Request
+
+ ### Development Guidelines
+ [Include specific guidelines based on project structure]
+
+ ## ๐ License
+
+ [Include license information if LICENSE file is found, otherwise suggest appropriate license]
+
+ ## ๐ Acknowledgments
+
+ [Include acknowledgments section]
+
+ ## ๐ Support & Contact
+
+ [Include support information]
+
+ ---
+
+ ## IMPORTANT INSTRUCTIONS:
+
+ 1. **Be Specific**: Use actual technologies, frameworks, and tools found in the project
+ 2. **Accurate Commands**: Provide correct installation and run commands based on package.json and configuration files
+ 3. **Real Features**: Derive features from actual code structure and components found
+ 4. **Professional Tone**: Write in a clear, professional, and engaging manner
+ 5. **Complete Information**: Ensure all sections are properly filled with relevant content
+ 6. **Modern Formatting**: Use proper markdown formatting, emojis, and code blocks
+ 7. **Actionable Content**: Provide specific, actionable instructions
+
+ The README should be comprehensive enough that a new developer can understand the project and get it running without additional documentation.
+ """
+
+ return self.generate_with_retry(readme_prompt)
+
+ def generate_with_retry(self, prompt, max_retries=10):
+ """Generate README with retry logic"""
+ print("๐ค Generating README with Claude AI...")
+ print(f"๐ Prompt length: {len(prompt):,} characters")
+ print(f"๐ฏ Using model: {self.model_id}")
+ print("=" * 80)
+
+ for attempt in range(max_retries):
+ try:
+ print(f"๐ Attempt {attempt + 1}/{max_retries}")
+
+ body = {
+ "anthropic_version": "bedrock-2023-05-31",
+ "max_tokens": 15000,
+ "messages": [
+ {
+ "role": "user",
+ "content": prompt
+ }
+ ]
+ }
+
+ start_time = time.time()
+ response = self.bedrock_client.invoke_model(
+ body=json.dumps(body),
+ modelId=self.model_id,
+ accept='application/json',
+ contentType='application/json'
+ )
+
+ response_body = json.loads(response.get('body').read())
+ readme_content = response_body['content'][0]['text']
+
+ end_time = time.time()
+ generation_time = end_time - start_time
+
+ print(f"โ
README generated in {generation_time:.2f} seconds!")
+ print(f"๐ Content length: {len(readme_content):,} characters")
+ print("=" * 80)
+
+ return readme_content
+
+ except Exception as e:
+ error_str = str(e)
+ print(f"โ Attempt {attempt + 1} failed: {error_str}")
+
+ if attempt < max_retries - 1:
+ delay = min(2 ** attempt + random.uniform(0, 1), 60)
+ print(f"โณ Waiting {delay:.2f} seconds before retry...")
+ time.sleep(delay)
+ else:
+ print("โ All retry attempts exhausted")
+ return None
+
+ return None
+
+ def save_readme(self, readme_content):
+ """Save README to the appropriate location"""
+ print("๐พ Saving README...")
+
+ # Create .github/claude directory if it doesn't exist
+ os.makedirs('.github/claude', exist_ok=True)
+
+ # Save the README
+ readme_file_path = '.github/claude/readme.md'
+ with open(readme_file_path, 'w', encoding='utf-8') as f:
+ f.write(readme_content)
+
+ print(f"โ
README saved to {readme_file_path}")
+ return readme_file_path
+
+ def create_branch_and_commit(self, readme_file_path):
+ """Create branch and commit README"""
+ try:
+ print(f"๐ฟ Creating branch: {self.branch_name}")
+
+ # Configure git
+ subprocess.run(['git', 'config', 'user.name', 'Claude README Generator'], check=True)
+ subprocess.run(['git', 'config', 'user.email', 'claude-readme@github-actions.bot'], check=True)
+
+ # Create and checkout new branch
+ subprocess.run(['git', 'checkout', '-b', self.branch_name], check=True)
+
+ # Add the README
+ subprocess.run(['git', 'add', readme_file_path], check=True)
+
+ # Create commit message
+ commit_msg = f"""๐ Generate comprehensive README with Claude AI
+
+ Auto-generated project documentation by Claude AI
+
+ Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ README Location: {readme_file_path}
+
+ This README includes:
+ ๐ Project overview and features
+ ๐ ๏ธ Complete tech stack analysis
+ ๐ Project structure documentation
+ ๐ง Installation and setup instructions
+ ๐ฏ Usage guidelines
+ ๐ค Contribution guidelines
+ """
+
+ # Commit changes
+ subprocess.run(['git', 'commit', '-m', commit_msg], check=True)
+
+ # Push branch
+ subprocess.run(['git', 'push', '-u', 'origin', self.branch_name], check=True)
+
+ print(f"โ
Successfully pushed README to branch: {self.branch_name}")
+ return True
+
+ except subprocess.CalledProcessError as e:
+ print(f"โ Git operation failed: {e}")
+ return False
+
+ def create_pull_request(self, readme_file_path):
+ """Create pull request for README"""
+ try:
+ print("๐ Creating pull request...")
+
+ github_client = Github(self.github_token)
+
+ # Create PR in the current repository only
+ target_repo_obj = github_client.get_repo(f"{self.repo_owner}/{self.repo_name}")
+ head_ref = self.branch_name
+ base_ref = self.target_branch
+ print(f"๐ฆ Creating PR: {head_ref} -> {base_ref}")
+
+ pr_title = f"๐ Claude AI Generated README - {datetime.now().strftime('%Y-%m-%d')}"
+
+ pr_body = f"""# ๐ Claude AI Generated README
+
+ This pull request contains a comprehensive README.md file automatically generated by Claude AI based on project analysis.
+
+ ## ๐ README Details
+ - **Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ - **Location:** `{readme_file_path}`
+ - **Analysis Scope:** Complete project structure and configuration
+
+ ## ๐ What's Included
+
+ โ
**Project Overview** - Clear description and purpose
+ โ
**Features List** - Key capabilities and functionality
+ โ
**Tech Stack** - Complete technology analysis
+ โ
**Project Structure** - Directory organization explanation
+ โ
**Installation Guide** - Step-by-step setup instructions
+ โ
**Usage Instructions** - Development and production commands
+ โ
**Contributing Guidelines** - How to contribute to the project
+ โ
**License Information** - Project licensing details
+
+ ## ๐ Analysis Summary
+
+ - **Total Files Analyzed:** {self.repo_name} project structure
+ - **Configuration Files:** package.json, tsconfig.json, capacitor.config.ts, etc.
+ - **Source Code:** React/Ionic TypeScript components and services
+ - **Build Tools:** Vite, Capacitor, and modern toolchain
+
+ ## ๐ Review Notes
+
+ - This README was generated using AI analysis of the entire codebase
+ - Please review the content for accuracy and completeness
+ - Feel free to edit or enhance any sections as needed
+ - The README follows modern documentation standards
+
+ ## ๐ Next Steps
+
+ 1. **Review** the generated README content
+ 2. **Verify** technical details and instructions
+ 3. **Customize** any project-specific information
+ 4. **Merge** when satisfied with the documentation
+
+ ---
+ *Generated automatically by Claude AI README Generator ๐ค*
+ """
+
+ # Try to create PR using PyGithub first
+ try:
+ pull_request = target_repo_obj.create_pull(
+ title=pr_title,
+ body=pr_body,
+ head=head_ref,
+ base=base_ref
+ )
+
+ # Add labels
+ try:
+ pull_request.add_to_labels('documentation', 'claude-generated', 'readme')
+ except:
+ pass # Labels might not exist
+
+ print(f"โ
Pull request created: {pull_request.html_url}")
+ return pull_request.html_url
+ except Exception as pr_error:
+ if "not permitted" in str(pr_error) or "403" in str(pr_error):
+ print("โ ๏ธ GitHub Actions cannot create PRs directly. Using GitHub CLI fallback...")
+
+ # Try using GitHub CLI
+ try:
+ cli_args = [
+ 'gh', 'pr', 'create',
+ '--title', pr_title,
+ '--body', pr_body,
+ '--head', head_ref,
+ '--base', base_ref
+ ]
+
+ result = subprocess.run(cli_args, capture_output=True, text=True, check=True)
+
+ pr_url = result.stdout.strip()
+ print(f"โ
Pull request created via GitHub CLI: {pr_url}")
+ return pr_url
+ except subprocess.CalledProcessError as cli_error:
+ print(f"โ GitHub CLI also failed: {cli_error}")
+
+ # Final fallback - create a summary file instead
+ print("๐ Creating README summary instead of PR...")
+ summary_path = self.create_readme_summary(readme_file_path)
+ print(f"โ
README summary created: {summary_path}")
+ print("๐ก You can manually create a PR from the claude-readme branch")
+ return f"Branch: {self.branch_name} (manual PR needed)"
+ else:
+ raise pr_error
+
+ except Exception as e:
+ print(f"โ Failed to create pull request: {e}")
+ # Create summary as fallback
+ summary_path = self.create_readme_summary(readme_file_path)
+ print(f"โ
README summary created instead: {summary_path}")
+ return None
+
+ def create_readme_summary(self, readme_file_path):
+ """Create a summary file when PR creation fails"""
+ summary_content = f"""# ๐ Claude AI README Generation Summary
+
+ **Generation Completed:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ **Branch:** {self.branch_name}
+ **README Location:** {readme_file_path}
+
+ ## ๐ What Was Generated
+
+ A comprehensive README.md file has been created with the following sections:
+ - Project overview and description
+ - Features and capabilities
+ - Complete tech stack analysis
+ - Project structure explanation
+ - Installation and setup instructions
+ - Usage guidelines
+ - Contributing guidelines
+ - License information
+
+ ## ๐ Next Steps
+
+ 1. **View the generated README:** `{readme_file_path}`
+ 2. **Create a Pull Request manually:**
+ - Go to your repository on GitHub
+ - Switch to the `{self.branch_name}` branch
+ - Click "Create Pull Request"
+ 3. **Review and customize** the README content as needed
+
+ ## ๐ Quick Links
+
+ - **Branch:** [{self.branch_name}](https://github.com/{self.repo_owner}/{self.repo_name}/tree/{self.branch_name})
+ - **README File:** [{readme_file_path}](https://github.com/{self.repo_owner}/{self.repo_name}/blob/{self.branch_name}/{readme_file_path})
+
+ ---
+ *Generated by Claude AI README Generator*
+ """
+
+ summary_path = 'README_GENERATION_SUMMARY.md'
+ with open(summary_path, 'w', encoding='utf-8') as f:
+ f.write(summary_content)
+
+ # Add and commit the summary
+ try:
+ subprocess.run(['git', 'add', summary_path], check=True)
+ subprocess.run(['git', 'commit', '-m', 'Add README generation summary'], check=True)
+ subprocess.run(['git', 'push'], check=True)
+ except:
+ pass
+
+ return summary_path
+
+ def run_generation(self):
+ """Main README generation execution method"""
+ print("๐ Starting Claude AI README Generation...")
+ print(f"๐ Detailed structure: {self.include_detailed_structure}")
+ print(f"๐ฌ Tech analysis: {self.include_tech_analysis}")
+ print(f"๐ Update existing: {self.update_existing}")
+ print("=" * 80)
+
+ # Step 1: Analyze project context
+ project_context = self.analyze_project_context()
+
+ # Step 2: Generate README with Claude
+ readme_content = self.generate_readme_with_claude(project_context)
+
+ if not readme_content:
+ print("โ Failed to generate README")
+ return False
+
+ # Step 3: Save README
+ readme_file_path = self.save_readme(readme_content)
+
+ # Step 4: Create branch and commit
+ if self.create_branch_and_commit(readme_file_path):
+ # Step 5: Create pull request
+ pr_url = self.create_pull_request(readme_file_path)
+
+ if pr_url:
+ print("=" * 80)
+ print("๐ README generation completed successfully!")
+ print(f"๐ README: {readme_file_path}")
+ print(f"๐ Pull Request: {pr_url}")
+ print("=" * 80)
+ return True
+ else:
+ print("=" * 80)
+ print("๐ README generation completed successfully!")
+ print(f"๐ README: {readme_file_path}")
+ print(f"๐ฟ Branch: {self.branch_name}")
+ print("๐ก Create a pull request manually from the GitHub interface")
+ print("=" * 80)
+ return True # Still consider this successful
+ else:
+ print("โ Failed to create branch and commit README")
+ return False
+
+ if __name__ == "__main__":
+ generator = ClaudeReadmeGenerator()
+ success = generator.run_generation()
+ exit(0 if success else 1)
+ EOF
+
+ python claude_readme_generator.py
+
+ - name: Summary
+ if: always()
+ run: |
+ echo "๐ Claude AI README Generator Summary"
+ echo "===================================="
+ echo "Detailed Structure: ${{ github.event.inputs.include_detailed_structure }}"
+ echo "Tech Analysis: ${{ github.event.inputs.include_tech_analysis }}"
+ echo "Update Existing: ${{ github.event.inputs.update_existing }}"
+ echo "Branch: claude-readme"
+ echo "README Location: .github/claude/readme.md"
+ echo ""
+ echo "๐ The README includes:"
+ echo " ๐ Comprehensive project overview and features"
+ echo " ๐ ๏ธ Complete tech stack analysis"
+ echo " ๐ Project structure documentation"
+ echo " ๐ง Installation and setup instructions"
+ echo " ๐ฏ Usage guidelines for development and production"
+ echo " ๐ค Contributing guidelines and standards"
+ echo ""
+ echo "๐ Check the pull request for the generated README documentation!"
diff --git a/.github/workflows/claude-test.yml b/.github/workflows/claude-test.yml
new file mode 100644
index 0000000..8d37281
--- /dev/null
+++ b/.github/workflows/claude-test.yml
@@ -0,0 +1,1281 @@
+name: Claude AI Test Generator
+
+on:
+ workflow_dispatch:
+ inputs:
+ test_type:
+ description: "Type of tests to generate"
+ required: false
+ default: "all"
+ type: choice
+ options:
+ - all
+ - unit
+ - integration
+ - e2e
+ - component
+ test_framework:
+ description: "Preferred testing framework"
+ required: false
+ default: "auto-detect"
+ type: choice
+ options:
+ - auto-detect
+ - jest
+ - vitest
+ - cypress
+ - playwright
+ include_test_readme:
+ description: "Generate README for tests"
+ required: false
+ default: true
+ type: boolean
+ coverage_target:
+ description: "Target test coverage percentage"
+ required: false
+ default: "80"
+ type: string
+
+jobs:
+ generate-tests:
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: write
+ pull-requests: write
+ actions: read
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Check and handle existing claude-test branch
+ id: check-branch
+ run: |
+ if git ls-remote --heads origin claude-test | grep -q claude-test; then
+ echo "branch_exists=true" >> $GITHUB_OUTPUT
+ echo "โ ๏ธ Branch 'claude-test' already exists!"
+ echo "๐๏ธ Automatically deleting existing branch..."
+ git push origin --delete claude-test || echo "Branch deletion failed, but continuing..."
+ echo "โ
Existing branch deleted. Proceeding with test generation."
+ else
+ echo "branch_exists=false" >> $GITHUB_OUTPUT
+ echo "โ
Branch 'claude-test' does not exist. Proceeding with test generation."
+ fi
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ${{ secrets.AWS_REGION }}
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "18"
+ cache: "npm"
+
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.11"
+
+ - name: Install dependencies
+ run: |
+ # Install Python dependencies
+ pip install boto3 requests botocore PyGithub
+
+ # Install GitHub CLI as backup for PR creation
+ type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
+ curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
+ && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
+ && sudo apt update \
+ && sudo apt install gh -y
+
+ # Install additional tools for analysis
+ sudo apt install -y tree file
+
+ # Try to install npm dependencies to understand project better
+ npm install || echo "npm install failed, but continuing with analysis"
+
+ - name: Analyze project for test generation
+ run: |
+ echo "๐ Analyzing project structure for test generation..."
+
+ # Create comprehensive file inventory for testing
+ find . -type f \( \
+ -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \
+ \) \
+ -not -path "./node_modules/*" \
+ -not -path "./.git/*" \
+ -not -path "./dist/*" \
+ -not -path "./build/*" \
+ -not -path "./coverage/*" \
+ -not -path "./.github/*" > source_files.txt
+
+ echo "๐ Found $(wc -l < source_files.txt) source files to analyze for testing"
+
+ # Find existing test files
+ find . -type f \( \
+ -name "*.test.ts" -o -name "*.test.tsx" -o -name "*.test.js" -o -name "*.test.jsx" -o \
+ -name "*.spec.ts" -o -name "*.spec.tsx" -o -name "*.spec.js" -o -name "*.spec.jsx" -o \
+ -name "*.e2e.ts" -o -name "*.e2e.js" -o -name "*cy.ts" -o -name "*cy.js" \
+ \) \
+ -not -path "./node_modules/*" \
+ -not -path "./.git/*" > existing_tests.txt
+
+ echo "๐งช Found $(wc -l < existing_tests.txt) existing test files"
+
+ # Generate project structure for testing context
+ echo "Project Structure for Testing:" > test_project_structure.txt
+ echo "==============================" >> test_project_structure.txt
+ tree -I 'node_modules|.git|dist|build|coverage|.next' -L 4 >> test_project_structure.txt 2>/dev/null || find . -type d -not -path "./node_modules*" -not -path "./.git*" | head -30 >> test_project_structure.txt
+
+ # Analyze testing configuration and dependencies
+ echo "Testing Configuration Analysis:" > test_config_analysis.txt
+ echo "===============================" >> test_config_analysis.txt
+
+ # Check for existing test configurations
+ test_configs=(
+ "jest.config.js"
+ "jest.config.ts"
+ "vitest.config.ts"
+ "vitest.config.js"
+ "cypress.config.ts"
+ "cypress.config.js"
+ "playwright.config.ts"
+ "playwright.config.js"
+ ".eslintrc.json"
+ "tsconfig.json"
+ "package.json"
+ )
+
+ for config in "${test_configs[@]}"; do
+ if [ -f "$config" ]; then
+ echo "=== $config ===" >> test_config_analysis.txt
+ head -100 "$config" >> test_config_analysis.txt
+ echo "" >> test_config_analysis.txt
+ fi
+ done
+
+ # Analyze source code patterns for test generation
+ echo "Source Code Analysis for Testing:" > source_code_patterns.txt
+ echo "==================================" >> source_code_patterns.txt
+
+ # Count different types of files that need testing
+ echo "Components to test:" >> source_code_patterns.txt
+ echo "React Components: $(find . -name "*.tsx" -not -path "./node_modules/*" | wc -l)" >> source_code_patterns.txt
+ echo "TypeScript files: $(find . -name "*.ts" -not -path "./node_modules/*" -not -name "*.test.*" -not -name "*.spec.*" | wc -l)" >> source_code_patterns.txt
+ echo "JavaScript files: $(find . -name "*.js" -not -path "./node_modules/*" -not -name "*.test.*" -not -name "*.spec.*" | wc -l)" >> source_code_patterns.txt
+ echo "Services/Utils: $(find . -path "*/services/*" -o -path "*/utils/*" -o -path "*/helpers/*" | grep -E '\.(ts|js)$' | wc -l)" >> source_code_patterns.txt
+ echo "Hooks: $(find . -name "*hook*" -o -name "*Hook*" | grep -E '\.(ts|tsx)$' | wc -l)" >> source_code_patterns.txt
+ echo "Contexts: $(find . -name "*context*" -o -name "*Context*" | grep -E '\.(ts|tsx)$' | wc -l)" >> source_code_patterns.txt
+
+ - name: Generate Tests with Claude AI
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ AWS_BEDROCK_MODEL_ID: ${{ secrets.AWS_BEDROCK_MODEL_ID }}
+ TEST_TYPE: ${{ github.event.inputs.test_type }}
+ TEST_FRAMEWORK: ${{ github.event.inputs.test_framework }}
+ INCLUDE_TEST_README: ${{ github.event.inputs.include_test_readme }}
+ COVERAGE_TARGET: ${{ github.event.inputs.coverage_target }}
+ REPO_OWNER: ${{ github.repository_owner }}
+ REPO_NAME: ${{ github.event.repository.name }}
+ TARGET_BRANCH: ${{ github.event.repository.default_branch }}
+ run: |
+ cat << 'EOF' > claude_test_generator.py
+ import boto3
+ import json
+ import os
+ import subprocess
+ import time
+ import random
+ from datetime import datetime
+ from botocore.config import Config
+ from github import Github
+ import re
+
+ class ClaudeTestGenerator:
+ def __init__(self):
+ self.github_token = os.environ['GITHUB_TOKEN']
+ self.bedrock_client = self.get_bedrock_client()
+ self.model_id = os.environ.get('AWS_BEDROCK_MODEL_ID', 'us.anthropic.claude-3-5-sonnet-20241022-v2:0')
+ self.test_type = os.environ.get('TEST_TYPE', 'all')
+ self.test_framework = os.environ.get('TEST_FRAMEWORK', 'auto-detect')
+ self.include_test_readme = os.environ.get('INCLUDE_TEST_README', 'true').lower() == 'true'
+ self.coverage_target = os.environ.get('COVERAGE_TARGET', '80')
+ self.repo_owner = os.environ['REPO_OWNER']
+ self.repo_name = os.environ['REPO_NAME']
+ self.target_branch = os.environ.get('TARGET_BRANCH', 'main')
+ self.branch_name = "claude-test"
+
+ def get_bedrock_client(self):
+ config = Config(
+ read_timeout=300,
+ connect_timeout=10,
+ retries={
+ 'max_attempts': 5,
+ 'mode': 'adaptive'
+ }
+ )
+ return boto3.client('bedrock-runtime', region_name=os.environ['AWS_DEFAULT_REGION'], config=config)
+
+ def read_file_safely(self, filepath, max_lines=150):
+ """Read file content safely, limiting lines for context"""
+ try:
+ with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
+ lines = f.readlines()
+ if len(lines) > max_lines:
+ return ''.join(lines[:max_lines]) + f'\n... (truncated, {len(lines)-max_lines} more lines)'
+ return ''.join(lines)
+ except Exception as e:
+ return f"Error reading file: {str(e)}"
+
+ def analyze_project_for_testing(self):
+ """Analyze the project structure and files for test generation"""
+ print("๐ Analyzing project for test generation...")
+
+ context = {
+ 'project_structure': '',
+ 'test_config_analysis': '',
+ 'source_code_patterns': '',
+ 'existing_tests': [],
+ 'source_files': [],
+ 'sample_source_files': {},
+ 'test_framework_detected': 'jest', # default
+ 'file_stats': {}
+ }
+
+ # Read project structure
+ if os.path.exists('test_project_structure.txt'):
+ context['project_structure'] = self.read_file_safely('test_project_structure.txt', 100)
+
+ # Read test configuration analysis
+ if os.path.exists('test_config_analysis.txt'):
+ context['test_config_analysis'] = self.read_file_safely('test_config_analysis.txt', 200)
+
+ # Read source code patterns
+ if os.path.exists('source_code_patterns.txt'):
+ context['source_code_patterns'] = self.read_file_safely('source_code_patterns.txt', 50)
+
+ # Read source files list
+ if os.path.exists('source_files.txt'):
+ with open('source_files.txt', 'r') as f:
+ context['source_files'] = [line.strip() for line in f.readlines() if line.strip()]
+
+ # Read existing tests
+ if os.path.exists('existing_tests.txt'):
+ with open('existing_tests.txt', 'r') as f:
+ context['existing_tests'] = [line.strip() for line in f.readlines() if line.strip()]
+
+ # Detect test framework from configuration
+ config_content = context['test_config_analysis'].lower()
+ if 'vitest' in config_content:
+ context['test_framework_detected'] = 'vitest'
+ elif 'cypress' in config_content:
+ context['test_framework_detected'] = 'cypress'
+ elif 'playwright' in config_content:
+ context['test_framework_detected'] = 'playwright'
+ elif 'jest' in config_content:
+ context['test_framework_detected'] = 'jest'
+
+ # Sample key source files for test generation
+ priority_patterns = [
+ r'src/.*[Pp]age.*\.(tsx?|jsx?)$', # Pages
+ r'src/.*[Cc]omponent.*\.(tsx?|jsx?)$', # Components
+ r'src/.*[Ss]ervice.*\.(tsx?|jsx?)$', # Services
+ r'src/.*[Uu]til.*\.(tsx?|jsx?)$', # Utils
+ r'src/.*[Hh]ook.*\.(tsx?|jsx?)$', # Hooks
+ r'src/.*[Cc]ontext.*\.(tsx?|jsx?)$', # Contexts
+ r'src/App\.(tsx?|jsx?)$', # Main App
+ r'src/main\.(tsx?|jsx?)$', # Main entry
+ ]
+
+ sampled_files = []
+ for pattern in priority_patterns:
+ for file_path in context['source_files']:
+ if re.search(pattern, file_path, re.IGNORECASE) and len(sampled_files) < 20:
+ sampled_files.append(file_path)
+
+ # Add some additional files if we don't have enough
+ for file_path in context['source_files'][:10]:
+ if len(sampled_files) < 25 and file_path not in sampled_files:
+ sampled_files.append(file_path)
+
+ # Read content of sampled files
+ for file_path in sampled_files:
+ if os.path.exists(file_path):
+ context['sample_source_files'][file_path] = self.read_file_safely(file_path, 100)
+
+ # Calculate file statistics
+ context['file_stats'] = {
+ 'total_source_files': len(context['source_files']),
+ 'existing_tests': len(context['existing_tests']),
+ 'components': len([f for f in context['source_files'] if f.endswith('.tsx')]),
+ 'typescript_files': len([f for f in context['source_files'] if f.endswith('.ts')]),
+ 'coverage_gap': len(context['source_files']) - len(context['existing_tests'])
+ }
+
+ print(f"โ
Analyzed {len(context['source_files'])} source files and {len(context['existing_tests'])} existing tests")
+ print(f"๐ Sampled {len(sampled_files)} files for test generation")
+ print(f"๐งช Detected test framework: {context['test_framework_detected']}")
+
+ return context
+
+ def generate_tests_with_claude(self, project_context):
+ """Generate comprehensive tests using Claude"""
+
+ test_type_descriptions = {
+ 'all': 'Complete test suite including unit, integration, and component tests',
+ 'unit': 'Unit tests for individual functions and methods',
+ 'integration': 'Integration tests for component interactions',
+ 'e2e': 'End-to-end tests for full user workflows',
+ 'component': 'Component tests for React components'
+ }
+
+ framework = self.test_framework if self.test_framework != 'auto-detect' else project_context['test_framework_detected']
+
+ test_prompt = f"""
+ You are an expert test engineer specializing in React, TypeScript, Ionic, and modern web application testing.
+ You need to generate comprehensive, high-quality tests for a government billing/invoicing application.
+
+ ## TEST GENERATION REQUIREMENTS
+
+ **Test Type:** {self.test_type} - {test_type_descriptions.get(self.test_type, 'Comprehensive testing')}
+ **Framework:** {framework}
+ **Coverage Target:** {self.coverage_target}%
+ **Repository:** {self.repo_owner}/{self.repo_name}
+
+ ## PROJECT ANALYSIS
+
+ ### File Statistics
+ - Total source files: {project_context['file_stats']['total_source_files']}
+ - Existing tests: {project_context['file_stats']['existing_tests']}
+ - React components: {project_context['file_stats']['components']}
+ - TypeScript files: {project_context['file_stats']['typescript_files']}
+ - Coverage gap: {project_context['file_stats']['coverage_gap']} files without tests
+
+ ### Project Structure
+ ```
+ {project_context['project_structure']}
+ ```
+
+ ### Testing Configuration
+ {project_context['test_config_analysis']}
+
+ ### Source Code Patterns
+ {project_context['source_code_patterns']}
+
+ ### Existing Tests
+ {chr(10).join(project_context['existing_tests']) if project_context['existing_tests'] else 'No existing tests found'}
+
+ ### Sample Source Files for Testing
+ """
+
+ # Add sample source files
+ for file_path, content in project_context['sample_source_files'].items():
+ test_prompt += f"\n**{file_path}:**\n```typescript\n{content}\n```\n"
+
+ test_prompt += f"""
+
+ ## TEST GENERATION INSTRUCTIONS
+
+ Generate comprehensive, production-ready tests based on the analyzed codebase. Follow these requirements:
+
+ ### Test Framework: {framework}
+
+ {self.get_framework_specific_instructions(framework)}
+
+ ### Test Types to Generate
+
+ {self.get_test_type_instructions()}
+
+ ## OUTPUT FORMAT
+
+ Provide your response in this exact format:
+
+ ### TEST_FILES_TO_CREATE
+
+ List each test file that should be created with its full path relative to .github/claude/tests/
+
+ ### TEST_FILE_CONTENT
+
+ For each test file, provide the complete content:
+
+ #### FILE: .github/claude/tests/[path]/[filename].test.ts
+ ```typescript
+ // Complete test file content here
+ ```
+
+ #### EXPLANATION: [filename].test.ts
+ [Brief explanation of what this test file covers]
+
+ ### TEST_CONFIGURATION
+
+ Provide any necessary test configuration files:
+
+ #### FILE: .github/claude/tests/test-setup.ts
+ ```typescript
+ // Test setup and configuration
+ ```
+
+ ### PACKAGE_JSON_UPDATES
+
+ Suggest any package.json script updates or dependencies needed for testing:
+
+ ```json
+ {{
+ "scripts": {{
+ "test": "...",
+ "test:watch": "...",
+ "test:coverage": "..."
+ }},
+ "devDependencies": {{
+ // Any additional testing dependencies
+ }}
+ }}
+ ```
+
+ ## TESTING BEST PRACTICES
+
+ 1. **Write Clear Test Names**: Use descriptive test names that explain what is being tested
+ 2. **Test Edge Cases**: Include tests for error conditions and edge cases
+ 3. **Mock External Dependencies**: Mock API calls, external services, and complex dependencies
+ 4. **Test User Interactions**: For components, test user interactions and state changes
+ 5. **Maintain Test Independence**: Each test should be independent and not rely on others
+ 6. **Use Proper Assertions**: Use specific assertions that clearly validate expected behavior
+ 7. **Group Related Tests**: Use describe blocks to group related tests logically
+ 8. **Setup and Teardown**: Include proper setup and cleanup for tests
+
+ ## IONIC/REACT SPECIFIC CONSIDERATIONS
+
+ - Test Ionic components and their native behavior
+ - Mock Capacitor plugins and native functionality
+ - Test responsive behavior and mobile-specific features
+ - Include accessibility testing where appropriate
+ - Test navigation and routing
+ - Mock platform-specific APIs
+
+ Generate comprehensive, maintainable tests that provide good coverage and catch real bugs!
+ """
+
+ return self.generate_with_retry(test_prompt)
+
+ def get_framework_specific_instructions(self, framework):
+ """Get framework-specific testing instructions"""
+ instructions = {
+ 'jest': """
+ - Use Jest testing framework with React Testing Library
+ - Use @testing-library/jest-dom for additional matchers
+ - Mock modules with jest.mock()
+ - Use jest.fn() for function mocking
+ - Configure proper test environment for React components
+ """,
+ 'vitest': """
+ - Use Vitest testing framework (Vite-native testing)
+ - Use @testing-library/react for component testing
+ - Use vi.mock() for module mocking
+ - Use vi.fn() for function mocking
+ - Configure Vitest for TypeScript and React
+ """,
+ 'cypress': """
+ - Use Cypress for end-to-end testing
+ - Write tests for complete user workflows
+ - Use cy.get(), cy.click(), cy.type() for interactions
+ - Test actual browser behavior and UI
+ - Include visual testing where appropriate
+ """,
+ 'playwright': """
+ - Use Playwright for end-to-end testing
+ - Test across multiple browsers
+ - Use page.locator() for element selection
+ - Include screenshot testing
+ - Test mobile and desktop viewports
+ """
+ }
+ return instructions.get(framework, instructions['jest'])
+
+ def get_test_type_instructions(self):
+ """Get instructions based on test type"""
+ if self.test_type == 'all':
+ return """
+ - **Unit Tests**: Test individual functions, utilities, and services
+ - **Component Tests**: Test React components in isolation
+ - **Integration Tests**: Test component interactions and data flow
+ - **E2E Tests**: Test complete user workflows (if framework supports it)
+ """
+ elif self.test_type == 'unit':
+ return """
+ - Focus on testing individual functions and methods
+ - Test utility functions, helpers, and services
+ - Mock all external dependencies
+ - Test edge cases and error conditions
+ """
+ elif self.test_type == 'component':
+ return """
+ - Test React components in isolation
+ - Test component props, state, and events
+ - Test user interactions with components
+ - Mock external dependencies and services
+ """
+ elif self.test_type == 'integration':
+ return """
+ - Test component interactions and data flow
+ - Test API integrations with mocked responses
+ - Test context providers and consumers
+ - Test routing and navigation
+ """
+ elif self.test_type == 'e2e':
+ return """
+ - Test complete user workflows
+ - Test critical business processes
+ - Test across different devices and browsers
+ - Include visual regression testing
+ """
+ return "Generate comprehensive tests covering all aspects of the application."
+
+ def generate_with_retry(self, prompt, max_retries=10):
+ """Generate tests with retry logic"""
+ print("๐ค Generating tests with Claude AI...")
+ print(f"๐ Prompt length: {len(prompt):,} characters")
+ print(f"๐ฏ Using model: {self.model_id}")
+ print(f"๐งช Test type: {self.test_type}")
+ print("=" * 80)
+
+ for attempt in range(max_retries):
+ try:
+ print(f"๐ Attempt {attempt + 1}/{max_retries}")
+
+ body = {
+ "anthropic_version": "bedrock-2023-05-31",
+ "max_tokens": 15000,
+ "messages": [
+ {
+ "role": "user",
+ "content": prompt
+ }
+ ]
+ }
+
+ start_time = time.time()
+ response = self.bedrock_client.invoke_model(
+ body=json.dumps(body),
+ modelId=self.model_id,
+ accept='application/json',
+ contentType='application/json'
+ )
+
+ response_body = json.loads(response.get('body').read())
+ test_content = response_body['content'][0]['text']
+
+ end_time = time.time()
+ generation_time = end_time - start_time
+
+ print(f"โ
Tests generated in {generation_time:.2f} seconds!")
+ print(f"๐ Content length: {len(test_content):,} characters")
+ print("=" * 80)
+
+ return test_content
+
+ except Exception as e:
+ error_str = str(e)
+ print(f"โ Attempt {attempt + 1} failed: {error_str}")
+
+ if attempt < max_retries - 1:
+ delay = min(2 ** attempt + random.uniform(0, 1), 60)
+ print(f"โณ Waiting {delay:.2f} seconds before retry...")
+ time.sleep(delay)
+ else:
+ print("โ All retry attempts exhausted")
+ return None
+
+ return None
+
+ def parse_and_save_tests(self, test_content):
+ """Parse Claude's response and save test files"""
+ print("๐พ Parsing and saving test files...")
+
+ # Create tests directory
+ tests_dir = '.github/claude/tests'
+ os.makedirs(tests_dir, exist_ok=True)
+
+ created_files = []
+
+ try:
+ # Extract test files using regex
+ file_pattern = r'#### FILE: ([^\n]+)\n```(?:typescript|javascript|json)?\n(.*?)\n```'
+ matches = re.findall(file_pattern, test_content, re.DOTALL)
+
+ if not matches:
+ print("โ ๏ธ No test file patterns found in Claude's response")
+ # Save the raw response as a reference
+ raw_file = os.path.join(tests_dir, 'claude_test_response.md')
+ with open(raw_file, 'w', encoding='utf-8') as f:
+ f.write(test_content)
+ created_files.append(raw_file)
+ return created_files
+
+ print(f"๐ Found {len(matches)} test files to create")
+
+ for i, (file_path, content) in enumerate(matches, 1):
+ # Clean up the file path
+ file_path = file_path.strip()
+ if not file_path.startswith('.github/claude/tests/'):
+ # Ensure the file is saved in the tests directory
+ filename = os.path.basename(file_path)
+ file_path = os.path.join(tests_dir, filename)
+
+ content = content.strip()
+
+ print(f"๐ Creating test file {i}/{len(matches)}: {file_path}")
+
+ # Create directory if it doesn't exist
+ dir_path = os.path.dirname(file_path)
+ if dir_path:
+ os.makedirs(dir_path, exist_ok=True)
+
+ # Write file content
+ with open(file_path, 'w', encoding='utf-8') as f:
+ f.write(content)
+
+ created_files.append(file_path)
+ print(f"โ
Created: {file_path}")
+
+ except Exception as e:
+ print(f"โ Error parsing test files: {e}")
+ # Save raw response as fallback
+ raw_file = os.path.join(tests_dir, 'claude_test_response.md')
+ with open(raw_file, 'w', encoding='utf-8') as f:
+ f.write(test_content)
+ created_files.append(raw_file)
+
+ return created_files
+
+ def create_test_readme(self, created_files, project_context):
+ """Create README for the tests directory"""
+ if not self.include_test_readme:
+ return None
+
+ print("๐ Creating test README...")
+
+ framework = self.test_framework if self.test_framework != 'auto-detect' else project_context['test_framework_detected']
+
+ readme_content = f"""# Claude AI Generated Tests
+
+ **Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ **Test Type:** {self.test_type}
+ **Framework:** {framework}
+ **Coverage Target:** {self.coverage_target}%
+
+ ## ๐ Overview
+
+ This directory contains comprehensive tests generated by Claude AI for the {self.repo_name} project.
+ The tests are designed to provide good coverage and catch real bugs in your application.
+
+ ## ๐งช Generated Test Files
+
+ """
+
+ for file_path in created_files:
+ filename = os.path.basename(file_path)
+ readme_content += f"- `{filename}` - {self.get_file_description(filename)}\n"
+
+ readme_content += f"""
+
+ ## ๐ Running Tests
+
+ ### Prerequisites
+
+ Make sure you have the necessary testing dependencies installed:
+
+ ```bash
+ npm install --save-dev {self.get_dependencies_for_framework(framework)}
+ ```
+
+ ### Running Tests in This Directory Only
+
+ To run only the tests in this directory:
+
+ ```bash
+ # Run all tests in this directory
+ {self.get_run_command_for_framework(framework, '.github/claude/tests')}
+
+ # Run tests with coverage
+ {self.get_coverage_command_for_framework(framework, '.github/claude/tests')}
+
+ # Run tests in watch mode
+ {self.get_watch_command_for_framework(framework, '.github/claude/tests')}
+ ```
+
+ ### Running Specific Test Files
+
+ ```bash
+ # Run a specific test file
+ {self.get_specific_file_command(framework)}
+ ```
+
+ ## ๐ Test Coverage
+
+ Target coverage: **{self.coverage_target}%**
+
+ To check coverage for these tests:
+
+ ```bash
+ {self.get_coverage_command_for_framework(framework, '.github/claude/tests')}
+ ```
+
+ ## ๐ง Test Configuration
+
+ ### Framework: {framework}
+
+ {self.get_framework_documentation(framework)}
+
+ ## ๐ Test Structure
+
+ Each test file follows these conventions:
+
+ - **Descriptive test names** that explain what is being tested
+ - **Proper setup and teardown** for clean test environments
+ - **Mocked dependencies** for isolated testing
+ - **Edge case testing** for robust error handling
+ - **Clear assertions** that validate expected behavior
+
+ ## ๐ฏ Test Types Generated
+
+ {self.get_test_types_documentation()}
+
+ ## ๐ Maintenance
+
+ ### Adding New Tests
+
+ 1. Follow the existing naming conventions
+ 2. Place tests in appropriate subdirectories
+ 3. Use the same testing patterns and setup
+ 4. Update this README if you add new categories
+
+ ### Updating Tests
+
+ When source code changes:
+
+ 1. Review related test files
+ 2. Update test cases as needed
+ 3. Ensure coverage targets are maintained
+ 4. Run the full test suite to ensure no regressions
+
+ ## ๐ Debugging Tests
+
+ ### Common Issues
+
+ 1. **Import errors**: Check that all dependencies are installed
+ 2. **Mock failures**: Verify that mocks match the actual API
+ 3. **Async issues**: Ensure proper async/await handling
+ 4. **Environment setup**: Check test environment configuration
+
+ ### Debug Commands
+
+ ```bash
+ # Run tests with verbose output
+ {self.get_verbose_command(framework)}
+
+ # Run tests with debugging
+ {self.get_debug_command(framework)}
+ ```
+
+ ## ๐ Resources
+
+ - [{framework} Documentation](https://example.com)
+ - [Testing Best Practices](https://example.com)
+ - [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)
+ - [Ionic Testing Guide](https://ionicframework.com/docs/testing/overview)
+
+ ## ๐ค Contributing
+
+ When contributing new tests:
+
+ 1. Follow the existing patterns and conventions
+ 2. Ensure tests are independent and can run in any order
+ 3. Use descriptive test names and clear assertions
+ 4. Include both positive and negative test cases
+ 5. Mock external dependencies appropriately
+
+ ---
+
+ *Tests generated by Claude AI - Review and customize as needed for your specific requirements*
+ """
+
+ readme_path = '.github/claude/tests/README.md'
+ with open(readme_path, 'w', encoding='utf-8') as f:
+ f.write(readme_content)
+
+ print(f"โ
Test README created: {readme_path}")
+ return readme_path
+
+ def get_file_description(self, filename):
+ """Get description for a test file based on its name"""
+ if 'component' in filename.lower():
+ return "Component tests"
+ elif 'service' in filename.lower():
+ return "Service layer tests"
+ elif 'util' in filename.lower() or 'helper' in filename.lower():
+ return "Utility function tests"
+ elif 'hook' in filename.lower():
+ return "React hooks tests"
+ elif 'context' in filename.lower():
+ return "Context provider tests"
+ elif 'e2e' in filename.lower() or 'integration' in filename.lower():
+ return "End-to-end tests"
+ else:
+ return "Application tests"
+
+ def get_dependencies_for_framework(self, framework):
+ """Get dependencies needed for the testing framework"""
+ deps = {
+ 'jest': '@testing-library/react @testing-library/jest-dom @testing-library/user-event jest jest-environment-jsdom',
+ 'vitest': '@testing-library/react @testing-library/jest-dom @testing-library/user-event vitest jsdom',
+ 'cypress': 'cypress @testing-library/cypress',
+ 'playwright': '@playwright/test'
+ }
+ return deps.get(framework, deps['jest'])
+
+ def get_run_command_for_framework(self, framework, path):
+ """Get command to run tests for specific framework and path"""
+ commands = {
+ 'jest': f'npx jest {path}',
+ 'vitest': f'npx vitest run {path}',
+ 'cypress': f'npx cypress run --spec "{path}/**/*"',
+ 'playwright': f'npx playwright test {path}'
+ }
+ return commands.get(framework, commands['jest'])
+
+ def get_coverage_command_for_framework(self, framework, path):
+ """Get command to run tests with coverage"""
+ commands = {
+ 'jest': f'npx jest {path} --coverage',
+ 'vitest': f'npx vitest run {path} --coverage',
+ 'cypress': f'npx cypress run --spec "{path}/**/*" (coverage via plugin)',
+ 'playwright': f'npx playwright test {path} --reporter=html'
+ }
+ return commands.get(framework, commands['jest'])
+
+ def get_watch_command_for_framework(self, framework, path):
+ """Get command to run tests in watch mode"""
+ commands = {
+ 'jest': f'npx jest {path} --watch',
+ 'vitest': f'npx vitest {path}',
+ 'cypress': f'npx cypress open',
+ 'playwright': f'npx playwright test {path} --ui'
+ }
+ return commands.get(framework, commands['jest'])
+
+ def get_specific_file_command(self, framework):
+ """Get command to run a specific test file"""
+ commands = {
+ 'jest': 'npx jest .github/claude/tests/specific-test.test.ts',
+ 'vitest': 'npx vitest run .github/claude/tests/specific-test.test.ts',
+ 'cypress': 'npx cypress run --spec ".github/claude/tests/specific-test.cy.ts"',
+ 'playwright': 'npx playwright test .github/claude/tests/specific-test.spec.ts'
+ }
+ return commands.get(framework, commands['jest'])
+
+ def get_verbose_command(self, framework):
+ """Get command to run tests with verbose output"""
+ commands = {
+ 'jest': 'npx jest --verbose',
+ 'vitest': 'npx vitest run --reporter=verbose',
+ 'cypress': 'npx cypress run --spec ".github/claude/tests/**/*" --reporter spec',
+ 'playwright': 'npx playwright test --reporter=list'
+ }
+ return commands.get(framework, commands['jest'])
+
+ def get_debug_command(self, framework):
+ """Get command to debug tests"""
+ commands = {
+ 'jest': 'node --inspect-brk node_modules/.bin/jest --runInBand',
+ 'vitest': 'npx vitest --inspect-brk',
+ 'cypress': 'npx cypress open (use browser dev tools)',
+ 'playwright': 'npx playwright test --debug'
+ }
+ return commands.get(framework, commands['jest'])
+
+ def get_framework_documentation(self, framework):
+ """Get framework-specific documentation"""
+ docs = {
+ 'jest': """
+ Jest is a JavaScript testing framework with a focus on simplicity.
+ - Uses describe() and test() functions to structure tests
+ - Built-in assertion library with expect()
+ - Powerful mocking capabilities with jest.mock()
+ - Snapshot testing for React components
+ """,
+ 'vitest': """
+ Vitest is a Vite-native testing framework that's fast and modern.
+ - Compatible with Jest API but faster
+ - Native TypeScript support
+ - Uses vi.mock() for mocking
+ - Built-in coverage reporting
+ """,
+ 'cypress': """
+ Cypress is an end-to-end testing framework for web applications.
+ - Tests run in a real browser
+ - Interactive test runner
+ - Time-travel debugging
+ - Real network traffic (can be stubbed)
+ """,
+ 'playwright': """
+ Playwright is a modern end-to-end testing framework.
+ - Cross-browser testing (Chrome, Firefox, Safari)
+ - Mobile testing support
+ - Auto-wait for elements
+ - Rich reporting and debugging
+ """
+ }
+ return docs.get(framework, docs['jest']).strip()
+
+ def get_test_types_documentation(self):
+ """Get documentation about generated test types"""
+ if self.test_type == 'all':
+ return """
+ - **Unit Tests**: Testing individual functions and methods in isolation
+ - **Component Tests**: Testing React components with user interactions
+ - **Integration Tests**: Testing component interactions and data flow
+ - **Service Tests**: Testing API calls and business logic
+ """
+ elif self.test_type == 'unit':
+ return "**Unit Tests**: Testing individual functions and methods in isolation"
+ elif self.test_type == 'component':
+ return "**Component Tests**: Testing React components with user interactions"
+ elif self.test_type == 'integration':
+ return "**Integration Tests**: Testing component interactions and data flow"
+ elif self.test_type == 'e2e':
+ return "**End-to-End Tests**: Testing complete user workflows"
+ return "Various test types based on the project structure"
+
+ def create_branch_and_commit(self, created_files):
+ """Create branch and commit test files"""
+ try:
+ print(f"๐ฟ Creating branch: {self.branch_name}")
+
+ # Configure git
+ subprocess.run(['git', 'config', 'user.name', 'Claude Test Generator'], check=True)
+ subprocess.run(['git', 'config', 'user.email', 'claude-test@github-actions.bot'], check=True)
+
+ # Create and checkout new branch
+ subprocess.run(['git', 'checkout', '-b', self.branch_name], check=True)
+
+ # Add all test files
+ for file_path in created_files:
+ subprocess.run(['git', 'add', file_path], check=True)
+
+ # Create commit message
+ commit_msg = f"""๐งช Generate comprehensive tests with Claude AI
+
+ Auto-generated test suite by Claude AI
+
+ Test Type: {self.test_type}
+ Framework: {self.test_framework}
+ Coverage Target: {self.coverage_target}%
+ Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ Files Created: {len(created_files)}
+
+ This test suite includes:
+ ๐งช {self.test_type} tests using {self.test_framework}
+ ๐ Test documentation and setup instructions
+ ๐ฏ Target coverage of {self.coverage_target}%
+ ๐ง Framework-specific configurations
+ """
+
+ # Commit changes
+ subprocess.run(['git', 'commit', '-m', commit_msg], check=True)
+
+ # Push branch
+ subprocess.run(['git', 'push', '-u', 'origin', self.branch_name], check=True)
+
+ print(f"โ
Successfully pushed tests to branch: {self.branch_name}")
+ return True
+
+ except subprocess.CalledProcessError as e:
+ print(f"โ Git operation failed: {e}")
+ return False
+
+ def create_pull_request(self, created_files):
+ """Create pull request for the generated tests"""
+ try:
+ print("๐ Creating pull request...")
+
+ github_client = Github(self.github_token)
+
+ # Create PR in the current repository only
+ target_repo_obj = github_client.get_repo(f"{self.repo_owner}/{self.repo_name}")
+ head_ref = self.branch_name
+ base_ref = self.target_branch
+ print(f"๐ฆ Creating PR: {head_ref} -> {base_ref}")
+
+ pr_title = f"๐งช Claude AI Generated Tests ({self.test_type}) - {datetime.now().strftime('%Y-%m-%d')}"
+
+ pr_body = f"""# ๐งช Claude AI Generated Test Suite
+
+ This pull request contains a comprehensive test suite automatically generated by Claude AI.
+
+ ## ๐ Test Details
+ - **Test Type:** {self.test_type}
+ - **Framework:** {self.test_framework}
+ - **Coverage Target:** {self.coverage_target}%
+ - **Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ - **Files Created:** {len(created_files)}
+
+ ## ๐งช Generated Tests
+
+ """
+
+ for file_path in created_files:
+ filename = os.path.basename(file_path)
+ pr_body += f"- `{filename}` - {self.get_file_description(filename)}\n"
+
+ pr_body += f"""
+
+ ## ๐ Running the Tests
+
+ Navigate to the tests directory and run:
+
+ ```bash
+ # Install testing dependencies (if not already installed)
+ npm install --save-dev {self.get_dependencies_for_framework(self.test_framework)}
+
+ # Run all tests in the generated directory
+ {self.get_run_command_for_framework(self.test_framework, '.github/claude/tests')}
+
+ # Run with coverage
+ {self.get_coverage_command_for_framework(self.test_framework, '.github/claude/tests')}
+ ```
+
+ ## ๐ Documentation
+
+ Check the `README.md` in the tests directory for:
+ - Detailed setup instructions
+ - Framework-specific commands
+ - Debugging guidelines
+ - Best practices
+
+ ## ๐ Review Notes
+
+ - Tests are generated based on project analysis and may need customization
+ - Review test assertions and mocks for accuracy
+ - Verify that all necessary dependencies are included
+ - Consider integrating these tests into your CI/CD pipeline
+
+ ## ๐ฏ Next Steps
+
+ 1. **Review** the generated test files
+ 2. **Run** the tests to ensure they pass
+ 3. **Customize** any project-specific test requirements
+ 4. **Integrate** into your testing workflow
+ 5. **Expand** test coverage as needed
+
+ ## ๐ Test Coverage Goal
+
+ These tests aim for **{self.coverage_target}%** coverage. Monitor actual coverage and adjust as needed.
+
+ ---
+ *Generated automatically by Claude AI Test Generator ๐ค*
+ """
+
+ # Try to create PR using PyGithub first
+ try:
+ pull_request = target_repo_obj.create_pull(
+ title=pr_title,
+ body=pr_body,
+ head=head_ref,
+ base=base_ref
+ )
+
+ # Add labels
+ try:
+ pull_request.add_to_labels('tests', 'claude-generated', 'testing')
+ except:
+ pass # Labels might not exist
+
+ print(f"โ
Pull request created: {pull_request.html_url}")
+ return pull_request.html_url
+ except Exception as pr_error:
+ if "not permitted" in str(pr_error) or "403" in str(pr_error):
+ print("โ ๏ธ GitHub Actions cannot create PRs directly. Using GitHub CLI fallback...")
+
+ # Try using GitHub CLI
+ try:
+ cli_args = [
+ 'gh', 'pr', 'create',
+ '--title', pr_title,
+ '--body', pr_body,
+ '--head', head_ref,
+ '--base', base_ref
+ ]
+
+ result = subprocess.run(cli_args, capture_output=True, text=True, check=True)
+
+ pr_url = result.stdout.strip()
+ print(f"โ
Pull request created via GitHub CLI: {pr_url}")
+ return pr_url
+ except subprocess.CalledProcessError as cli_error:
+ print(f"โ GitHub CLI also failed: {cli_error}")
+
+ # Final fallback - create a summary file instead
+ print("๐ Creating test summary instead of PR...")
+ summary_path = self.create_test_summary(created_files)
+ print(f"โ
Test summary created: {summary_path}")
+ print("๐ก You can manually create a PR from the claude-test branch")
+ return f"Branch: {self.branch_name} (manual PR needed)"
+ else:
+ raise pr_error
+
+ except Exception as e:
+ print(f"โ Failed to create pull request: {e}")
+ # Create summary as fallback
+ summary_path = self.create_test_summary(created_files)
+ print(f"โ
Test summary created instead: {summary_path}")
+ return None
+
+ def create_test_summary(self, created_files):
+ """Create a summary file when PR creation fails"""
+ summary_content = f"""# ๐งช Claude AI Test Generation Summary
+
+ **Generation Completed:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
+ **Branch:** {self.branch_name}
+ **Test Type:** {self.test_type}
+ **Framework:** {self.test_framework}
+ **Files Created:** {len(created_files)}
+
+ ## ๐ Generated Test Files
+
+ """
+
+ for file_path in created_files:
+ summary_content += f"- `{file_path}`\n"
+
+ summary_content += f"""
+
+ ## ๐ Running Tests
+
+ Navigate to the tests directory:
+ ```bash
+ cd .github/claude/tests
+ {self.get_run_command_for_framework(self.test_framework, '.')}
+ ```
+
+ ## ๐ Next Steps
+
+ 1. **Review the generated tests** in `.github/claude/tests/`
+ 2. **Read the README** for detailed setup instructions
+ 3. **Create a Pull Request manually:**
+ - Go to your repository on GitHub
+ - Switch to the `{self.branch_name}` branch
+ - Click "Create Pull Request"
+ 4. **Run the tests** to ensure they work correctly
+
+ ## ๐ Quick Links
+
+ - **Branch:** [{self.branch_name}](https://github.com/{self.repo_owner}/{self.repo_name}/tree/{self.branch_name})
+ - **Tests Directory:** [.github/claude/tests](https://github.com/{self.repo_owner}/{self.repo_name}/tree/{self.branch_name}/.github/claude/tests)
+
+ ---
+ *Generated by Claude AI Test Generator*
+ """
+
+ summary_path = 'TEST_GENERATION_SUMMARY.md'
+ with open(summary_path, 'w', encoding='utf-8') as f:
+ f.write(summary_content)
+
+ # Add and commit the summary
+ try:
+ subprocess.run(['git', 'add', summary_path], check=True)
+ subprocess.run(['git', 'commit', '-m', 'Add test generation summary'], check=True)
+ subprocess.run(['git', 'push'], check=True)
+ except:
+ pass
+
+ return summary_path
+
+ def run_test_generation(self):
+ """Main test generation execution method"""
+ print("๐งช Starting Claude AI Test Generation...")
+ print(f"๐ฏ Test type: {self.test_type}")
+ print(f"๐ ๏ธ Framework: {self.test_framework}")
+ print(f"๐ Coverage target: {self.coverage_target}%")
+ print(f"๐ Include README: {self.include_test_readme}")
+ print("=" * 80)
+
+ # Step 1: Analyze project for testing
+ project_context = self.analyze_project_for_testing()
+
+ # Step 2: Generate tests with Claude
+ test_content = self.generate_tests_with_claude(project_context)
+
+ if not test_content:
+ print("โ Failed to generate tests")
+ return False
+
+ # Step 3: Parse and save test files
+ created_files = self.parse_and_save_tests(test_content)
+
+ # Step 4: Create test README
+ readme_path = self.create_test_readme(created_files, project_context)
+ if readme_path:
+ created_files.append(readme_path)
+
+ # Step 5: Create branch and commit
+ if self.create_branch_and_commit(created_files):
+ # Step 6: Create pull request
+ pr_url = self.create_pull_request(created_files)
+
+ if pr_url:
+ print("=" * 80)
+ print("๐ Test generation completed successfully!")
+ print(f"๐ Tests directory: .github/claude/tests")
+ print(f"๐ Files created: {len(created_files)}")
+ print(f"๐ Pull Request: {pr_url}")
+ print("=" * 80)
+ return True
+ else:
+ print("=" * 80)
+ print("๐ Test generation completed successfully!")
+ print(f"๐ Tests directory: .github/claude/tests")
+ print(f"๐ Files created: {len(created_files)}")
+ print(f"๐ฟ Branch: {self.branch_name}")
+ print("๐ก Create a pull request manually from the GitHub interface")
+ print("=" * 80)
+ return True # Still consider this successful
+ else:
+ print("โ Failed to create branch and commit test files")
+ return False
+
+ if __name__ == "__main__":
+ generator = ClaudeTestGenerator()
+ success = generator.run_test_generation()
+ exit(0 if success else 1)
+ EOF
+
+ python claude_test_generator.py
+
+ - name: Summary
+ if: always()
+ run: |
+ echo "๐งช Claude AI Test Generator Summary"
+ echo "=================================="
+ echo "Test Type: ${{ github.event.inputs.test_type }}"
+ echo "Framework: ${{ github.event.inputs.test_framework }}"
+ echo "Coverage Target: ${{ github.event.inputs.coverage_target }}%"
+ echo "Include README: ${{ github.event.inputs.include_test_readme }}"
+ echo "Branch: claude-test"
+ echo "Tests Location: .github/claude/tests/"
+ echo ""
+ echo "๐งช Generated tests include:"
+ echo " ๐ Comprehensive test coverage analysis"
+ echo " ๐ฌ Framework-specific test implementations"
+ echo " ๐ Detailed README with run instructions"
+ echo " ๐ฏ Targeted coverage goals"
+ echo " ๐ง Proper setup and configuration"
+ echo ""
+ echo "๐ Check the pull request for the generated test suite!"