Contributing to Copilot Session Viewer development.
- Node.js ≥ 18.0.0 (LTS recommended)
- npm ≥ 9.0.0 or yarn ≥ 1.22.0
- Git for version control
- GitHub Copilot CLI for testing
# Fork on GitHub, then clone your fork
git clone https://github.com/YOUR_USERNAME/copilot-session-viewer.git
cd copilot-session-viewer
# Add upstream remote
git remote add upstream https://github.com/qiaolei81/copilot-session-viewer.git# Install dependencies
npm install
# Copy environment template (optional)
cp .env.example .env
# Start development server with hot reload
npm run devThe development server will start at http://localhost:3838 with:
- Auto-reload - Server restarts on file changes
- Template hot-reload - EJS templates update without restart
- Verbose logging - Detailed debug information
copilot-session-viewer/
├── 📁 src/ # Source code
│ ├── 📁 controllers/ # Express route controllers
│ │ ├── sessionController.js # Session management
│ │ └── insightController.js # AI insight generation
│ ├── 📁 middleware/ # Express middleware
│ │ ├── common.js # Common middleware (CORS, timeout, etc.)
│ │ └── rateLimiting.js # Rate limiting configuration
│ ├── 📁 models/ # Data models
│ │ └── Session.js # Session data model
│ ├── 📁 services/ # Business logic
│ │ ├── sessionRepository.js # Session data access
│ │ ├── sessionService.js # Session business logic
│ │ └── insightService.js # AI insight generation
│ ├── 📁 utils/ # Utility functions
│ │ ├── fileUtils.js # File system utilities
│ │ ├── helpers.js # General helpers
│ │ └── processManager.js # Process management
│ ├── app.js # Express app configuration
│ └── config.js # Configuration management
├── 📁 views/ # EJS templates
│ ├── index.ejs # Homepage with session list
│ ├── session-vue.ejs # Vue.js session detail view
│ └── time-analyze.ejs # Time analysis dashboard
├── 📁 __tests__/ # Test suite
│ ├── 📁 e2e/ # End-to-end tests (Playwright)
│ ├── server.test.js # API endpoint tests
│ ├── sessionRepository.test.js # Data layer tests
│ └── ... # Unit tests
├── 📁 docs/ # Documentation
├── server.js # Application entry point
├── package.json # Dependencies and scripts
├── CHANGELOG.md # Version history
└── README.md # Main documentation
-
Sync with upstream:
git fetch upstream git checkout main git merge upstream/main
-
Create feature branch:
git checkout -b feature/your-feature-name
-
Make changes & test:
# Start development server npm run dev # In another terminal, run tests npm test npm run test:e2e
-
Commit & push:
git add . git commit -m "feat: add your feature description" git push origin feature/your-feature-name
-
Create pull request on GitHub
General Principles:
- Consistency - Follow existing patterns
- Readability - Clear variable and function names
- Documentation - Comment complex logic
JavaScript Style:
// ✅ Good
const sessionService = require('./services/sessionService');
async function getSessionList(req, res) {
try {
const sessions = await sessionService.getAllSessions();
res.json(sessions);
} catch (error) {
console.error('Error loading sessions:', error);
res.status(500).json({ error: 'Failed to load sessions' });
}
}
// ❌ Avoid
const ss=require('./services/sessionService');
function getSessionList(req,res){
sessionService.getAllSessions().then(s=>res.json(s)).catch(e=>res.status(500).json({error:e}));
}Formatting Rules:
- Indentation: 2 spaces (no tabs)
- Quotes: Single quotes for JavaScript, double quotes for HTML/EJS
- Semicolons: Always required
- Line length: 100 characters max
- Async/Await: Preferred over Promises
Linting:
# Check code style
npm run lint
# Auto-fix style issues
npm run lint -- --fix__tests__/
├── 📁 e2e/ # End-to-end tests
│ ├── homepage.spec.js # Homepage functionality
│ ├── session-detail.spec.js # Session viewer
│ └── api.spec.js # API endpoints
├── server.test.js # Express app integration tests
├── sessionRepository.test.js # Data layer unit tests
├── fileUtils.test.js # Utility function tests
└── helpers.test.js # Helper function tests
# Run all unit tests
npm test
# Run tests in watch mode
npm test -- --watch
# Run specific test file
npm test -- sessionRepository.test.js
# Run end-to-end tests
npm run test:e2e
# Run e2e tests in headed mode (visible browser)
npm run test:e2e -- --headed
# Generate test coverage
npm run test:coverageUnit Test Example:
// __tests__/helpers.test.js
const { isValidSessionId } = require('../src/utils/helpers');
describe('helpers', () => {
describe('isValidSessionId', () => {
it('should accept valid UUIDs', () => {
expect(isValidSessionId('f9db650c-1f87-491f-8e4f-52d45538d677')).toBe(true);
});
it('should reject invalid formats', () => {
expect(isValidSessionId('invalid-id')).toBe(false);
expect(isValidSessionId('')).toBe(false);
expect(isValidSessionId(null)).toBe(false);
});
});
});E2E Test Example:
// __tests__/e2e/homepage.spec.js
const { test, expect } = require('@playwright/test');
test('should load homepage successfully', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle('Copilot Session Viewer');
await expect(page.locator('h1')).toContainText('Session Viewer');
// Check for session input
const sessionInput = page.locator('input[placeholder*="Session ID"]');
await expect(sessionInput).toBeVisible();
});For testing, you can create mock session data:
# Create test session directory
mkdir -p ~/.copilot/session-state/test-session-id
# Create mock events file
cat > ~/.copilot/session-state/test-session-id/events.jsonl << 'EOF'
{"type":"session.start","timestamp":"2026-02-15T12:00:00.000Z"}
{"type":"user.message","timestamp":"2026-02-15T12:00:01.000Z","message":"Hello"}
{"type":"assistant.message","timestamp":"2026-02-15T12:00:05.000Z","message":"Hi there!"}
EOF
# Create mock workspace file
cat > ~/.copilot/session-state/test-session-id/workspace.yaml << 'EOF'
cwd: /tmp/test
copilot_version: 0.0.410
model: claude-sonnet-4.5
EOFEnable debug logging:
DEBUG=copilot-viewer:* npm run devNode.js debugging with VS Code:
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Server",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/server.js",
"env": {
"NODE_ENV": "development",
"DEBUG": "copilot-viewer:*"
}
}
]
}Browser debugging:
// Add breakpoints in EJS templates
<script>
console.log('Session data:', sessions);
debugger; // Browser will pause here when DevTools is open
</script># Debug specific session parsing
node -e "
const Session = require('./src/models/Session');
Session.fromDirectory('~/.copilot/session-state/YOUR_SESSION_ID')
.then(console.log)
.catch(console.error);
"// In code, use appropriate log levels
console.error('Critical error:', error); // Always shown
console.warn('Warning:', warning); // Production + development
console.log('Info:', info); // Development only
console.debug('Debug details:', details); // Debug mode only- Check existing issues - Avoid duplicate work
- Discuss major changes - Open an issue first for big features
- Follow conventions - Match existing code style and patterns
Use Conventional Commits:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types:
feat:- New featurefix:- Bug fixdocs:- Documentation changesstyle:- Code style changes (formatting, etc.)refactor:- Code refactoringtest:- Adding or updating testschore:- Maintenance tasks
Examples:
feat: add infinite scroll to session list
fix: resolve session parsing error for empty files
docs: update installation instructions
test: add e2e tests for session export- Update documentation - If you change functionality
- Add tests - For new features or bug fixes
- Ensure tests pass - Both unit and e2e tests
- Update changelog - Add entry to
CHANGELOG.md - Request review - Tag maintainers for review
For Authors:
- Tests pass locally
- Code follows style guidelines
- Documentation is updated
- Changelog is updated
- No console errors in browser
- No linting errors
For Reviewers:
- Code is readable and maintainable
- Tests cover the changes
- No security issues
- Performance is acceptable
- UI/UX is consistent
- Documentation is accurate
This project uses Semantic Versioning:
- MAJOR version when you make incompatible API changes
- MINOR version when you add functionality in a backward compatible manner
- PATCH version when you make backward compatible bug fixes
-
Update version:
npm version patch # or minor, major -
Update changelog:
# Move [Unreleased] section to new version # Update version links at bottom
-
Create release:
git push origin main --tags
-
Publish to npm:
npm publish
-
Create GitHub release with changelog notes
Backend: Node.js + Express
- ✅ JavaScript ecosystem consistency
- ✅ Excellent file system handling
- ✅ Large middleware ecosystem
- ✅ Easy deployment
Frontend: EJS + Vue 3
- ✅ Server-side rendering for SEO
- ✅ Progressive enhancement
- ✅ Vue 3 for complex interactions
- ✅ No build process for simple templates
Data: File System
- ✅ No database setup required
- ✅ Direct access to Copilot CLI data
- ✅ Easy backup and sharing
- ✅ Minimal dependencies
Repository Pattern (src/services/sessionRepository.js)
- Abstracts data access
- Enables testing with mock data
- Consistent interface
Controller Pattern (src/controllers/)
- Separates request handling from business logic
- Clean error handling
- Consistent response formatting
Service Layer (src/services/)
- Business logic separation
- Reusable components
- Dependency injection
- Virtual Scrolling - Handle large event lists
- Infinite Scroll - Progressive loading
- Debounced Search - Reduce API calls
- CSS Containment - Improve rendering performance
- Streaming JSONL - Memory-efficient parsing
- File System Caching - Reduce disk I/O
- Rate Limiting - Prevent abuse
- Compression - Reduce bandwidth
// Add performance monitoring
const startTime = Date.now();
// ... operation
const duration = Date.now() - startTime;
console.log(`Operation took ${duration}ms`);// Always validate session IDs
const { isValidSessionId } = require('../utils/helpers');
if (!isValidSessionId(sessionId)) {
return res.status(400).json({ error: 'Invalid session ID' });
}// Prevent path traversal attacks
const path = require('path');
const sessionPath = path.resolve(SESSION_DIR, sessionId);
// Ensure path is within session directory
if (!sessionPath.startsWith(path.resolve(SESSION_DIR))) {
return res.status(400).json({ error: 'Invalid path' });
}Currently configured in src/app.js - be careful when modifying CSP headers.
- API Documentation - REST endpoint details
- Troubleshooting - Common issues
- Installation Guide - Setup instructions
- 💬 GitHub Discussions - Questions and ideas
- 🐛 GitHub Issues - Bug reports and feature requests
For sensitive security issues, contact the maintainers directly.
Happy coding! 🚀 Thank you for contributing to Copilot Session Viewer!