155 lines
6.3 KiB
Markdown
155 lines
6.3 KiB
Markdown
|
|
# AGENTS.md
|
||
|
|
|
||
|
|
## Build, Test & Run Commands
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Build entire solution
|
||
|
|
dotnet build EngineeringSync.slnx
|
||
|
|
|
||
|
|
# Run the background service (dev mode)
|
||
|
|
dotnet run --project EngineeringSync.Service
|
||
|
|
|
||
|
|
# Run the WPF tray app
|
||
|
|
dotnet run --project EngineeringSync.TrayApp
|
||
|
|
|
||
|
|
# Run all tests
|
||
|
|
dotnet test
|
||
|
|
|
||
|
|
# Run a single test project
|
||
|
|
dotnet test EngineeringSync.Tests --filter "FullyQualifiedName~WatcherServiceTests"
|
||
|
|
|
||
|
|
# Run tests with verbose output
|
||
|
|
dotnet test --verbosity normal
|
||
|
|
|
||
|
|
# Build Release
|
||
|
|
dotnet build EngineeringSync.slnx -c Release
|
||
|
|
|
||
|
|
# Install as Windows Service (production)
|
||
|
|
sc create EngineeringSync binPath="<path>\EngineeringSync.Service.exe"
|
||
|
|
|
||
|
|
# Build installer (requires Inno Setup 6)
|
||
|
|
.\installer\build-installer.ps1
|
||
|
|
```
|
||
|
|
|
||
|
|
## Code Style Guidelines
|
||
|
|
|
||
|
|
### General Principles
|
||
|
|
- Use C# 12+ features (file-scoped namespaces, collection expressions, primary constructors)
|
||
|
|
- Use `[ObservableProperty]` and `[RelayCommand]` attributes from CommunityToolkit.Mvvm
|
||
|
|
- Prefer source generators over boilerplate code
|
||
|
|
|
||
|
|
### Imports
|
||
|
|
- Sort by: System namespaces first, then Microsoft, then third-party, then project-specific
|
||
|
|
- Use file-scoped namespaces: `namespace EngineeringSync.Domain.Entities;`
|
||
|
|
- Global using statements in each project for common types
|
||
|
|
|
||
|
|
### Types & Collections
|
||
|
|
- Use collection expressions `[]` instead of `new List<T>()` where possible
|
||
|
|
- Use `IReadOnlyList<T>` for parameters that should not be modified
|
||
|
|
- Use `Guid` for all ID types
|
||
|
|
- Use `DateTime.UtcNow` for timestamps, convert to local time in UI layer only
|
||
|
|
|
||
|
|
### Naming Conventions
|
||
|
|
- **Classes/Interfaces:** PascalCase (e.g., `WatcherService`, `IApiClient`)
|
||
|
|
- **Methods:** PascalCase (e.g., `StartWatchingAsync`)
|
||
|
|
- **Private fields:** camelCase (e.g., `_watchers`, `_channel`)
|
||
|
|
- **Properties:** PascalCase (e.g., `IsActive`, `EngineeringPath`)
|
||
|
|
- **Parameters:** camelCase (e.g., `project`, `stoppingToken`)
|
||
|
|
- **Constants:** PascalCase (e.g., `DefaultTimeout`)
|
||
|
|
|
||
|
|
### Records vs Classes
|
||
|
|
- Use `record` for DTOs and API models
|
||
|
|
- Use `class` for entities with mutable properties
|
||
|
|
|
||
|
|
### Error Handling
|
||
|
|
- Use `try/catch` with specific exception types
|
||
|
|
- Log exceptions with context using `ILogger` (use structured logging: `logger.LogError(ex, "Message {Param}", param)`)
|
||
|
|
- In ViewModels, catch exceptions and set `StatusMessage` for user feedback
|
||
|
|
|
||
|
|
### Async/Await
|
||
|
|
- Always use `Async` suffix for async methods
|
||
|
|
- Pass `CancellationToken` to all cancellable operations
|
||
|
|
- Use `await using` for `IDisposable` resources that implement `IAsyncDisposable`
|
||
|
|
|
||
|
|
### Entity Framework Core
|
||
|
|
- Use `IDbContextFactory<AppDbContext>` for scoped DbContext in background services
|
||
|
|
- Use `await using var db = await dbFactory.CreateDbContextAsync(ct);`
|
||
|
|
- Configure SQLite with WAL mode for better concurrency
|
||
|
|
|
||
|
|
### WPF / MVVM
|
||
|
|
- ViewModels inherit from `ObservableObject` (CommunityToolkit.Mvvm)
|
||
|
|
- Use `[ObservableProperty]` for properties that need change notification
|
||
|
|
- Use `[RelayCommand]` for commands
|
||
|
|
- Use `partial class` for generated properties
|
||
|
|
|
||
|
|
### Dependency Injection
|
||
|
|
- Constructor injection with primary constructors:
|
||
|
|
```csharp
|
||
|
|
public sealed class WatcherService(
|
||
|
|
IDbContextFactory<AppDbContext> dbFactory,
|
||
|
|
IHubContext<NotificationHub> hub,
|
||
|
|
ILogger<WatcherService> logger) : BackgroundService
|
||
|
|
```
|
||
|
|
|
||
|
|
### File Organization
|
||
|
|
```
|
||
|
|
EngineeringSync.Domain/ - Entities, Enums, Interfaces
|
||
|
|
EngineeringSync.Infrastructure/ - EF Core, DbContext, Migrations
|
||
|
|
EngineeringSync.Service/ - API, Hubs, Background Services
|
||
|
|
EngineeringSync.TrayApp/ - WPF Views, ViewModels, Services
|
||
|
|
EngineeringSync.Setup/ - Installer wizard
|
||
|
|
```
|
||
|
|
|
||
|
|
### Key Patterns
|
||
|
|
|
||
|
|
**FileSystemWatcher Debouncing:** Events flow into a `Channel<FileEvent>`, consumer groups by `(ProjectId, RelativePath)` within 2000ms window, SHA-256 confirms actual changes.
|
||
|
|
|
||
|
|
**SignalR Notifications:** `NotificationHub` at `/notifications` broadcasts `ReceiveChangeNotification(projectId, projectName, count)` to all clients.
|
||
|
|
|
||
|
|
**Backup before sync:** Use timestamped naming `{filename}_{yyyyMMdd_HHmmss}.bak`.
|
||
|
|
|
||
|
|
### Service API Endpoints
|
||
|
|
- `GET/POST/PUT/DELETE /api/projects` - Project CRUD
|
||
|
|
- `GET /api/changes/{projectId}` - Get pending changes
|
||
|
|
- `GET /api/changes/{projectId}/history` - Get synced/ignored changes
|
||
|
|
- `POST /api/sync` - Sync selected changes
|
||
|
|
- `POST /api/ignore` - Ignore selected changes
|
||
|
|
|
||
|
|
### Database
|
||
|
|
- SQLite with WAL mode
|
||
|
|
- Entities: `ProjectConfig`, `FileRevision`, `PendingChange`
|
||
|
|
- `ProjectConfig.FileExtensions` is comma-separated (e.g., ".jt,.cojt,.xml")
|
||
|
|
|
||
|
|
## Solution Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
EngineeringSync.slnx
|
||
|
|
├── EngineeringSync.Domain/ (Class Library, net10.0) - Entities, Enums, Interfaces
|
||
|
|
├── EngineeringSync.Infrastructure/ (Class Library, net10.0) - EF Core, DbContext
|
||
|
|
├── EngineeringSync.Service/ (Worker Service, net10.0) - Kestrel on :5050, SignalR hub
|
||
|
|
├── EngineeringSync.TrayApp/ (WPF App, net10.0-windows) - System tray + UI windows
|
||
|
|
├── EngineeringSync.Setup/ (WPF App, net10.0-windows) - Setup wizard + Installer
|
||
|
|
└── installer/
|
||
|
|
├── setup.iss - Inno Setup Script
|
||
|
|
└── build-installer.ps1 - Build + Publish + ISCC Pipeline
|
||
|
|
```
|
||
|
|
|
||
|
|
## Tech Stack
|
||
|
|
|
||
|
|
- **Framework:** .NET 10
|
||
|
|
- **Service:** Worker Service (Windows Service) + ASP.NET Core Minimal API + SignalR
|
||
|
|
- **Client:** WPF (.NET 10-Windows) with `H.NotifyIcon.Wpf` + `CommunityToolkit.Mvvm`
|
||
|
|
- **Database:** SQLite via EF Core 10 (WAL mode)
|
||
|
|
- **File Watching:** `System.IO.FileSystemWatcher` + `System.Threading.Channels` (debouncing)
|
||
|
|
|
||
|
|
## Key Architecture
|
||
|
|
|
||
|
|
- **ProjectConfig in DB:** Managed via TrayApp UI, stored in SQLite. User adds/edits/deletes projects with folder browser dialogs. CRUD operations go through Service API, which dynamically starts/stops watchers.
|
||
|
|
- **FileSystemWatcher Debouncing:** Events (Created, Changed, Renamed, Deleted) pushed into `Channel<FileEvent>`. Consumer groups events by `(ProjectId, RelativePath)` within 2000ms sliding window. SHA-256 hashing against `FileRevision` confirms actual changes before writing a `PendingChange`.
|
||
|
|
- **SignalR Hub:** `NotificationHub` at `/notifications` broadcasts `ReceiveChangeNotification(projectId, projectName, count)` and `ProjectConfigChanged()`.
|
||
|
|
|
||
|
|
## Notes
|
||
|
|
- Domain project has no dependencies (pure entities/enums)
|
||
|
|
- Infrastructure depends on Domain
|
||
|
|
- Service depends on Domain + Infrastructure
|
||
|
|
- TrayApp depends on Domain + Service (via HTTP client)
|