net10.0 throughout; corrected EntryKey field name; documented *.Services split. Two new files (DeepDrftWeb.Services, DeepDrftContent.Services). FileDatabase README inaccuracies fixed (ImageVault, project target).
7.9 KiB
CLAUDE.md - DeepDrftCli
Guidance for working in the DeepDrftCli project (the admin CLI tool).
See the root CLAUDE.md for full architecture overview. This file covers what is specific to this project.
One-line purpose
Local admin tool. Adds and lists tracks. Two modes: classic CLI (add / list / help) and Terminal.Gui interactive interface (gui). Has direct access to both the SQL DB and the FileDatabase (it's not a network client — it runs on the same machine as the databases).
Why this is allowed to bypass the API
CLI is a local single-user admin tool, run on the same machine as the databases by an admin. It consumes DeepDrftWeb.Services and DeepDrftContent.Services directly without going through HTTP. Browsers never get to bypass — the APIs are for them. Don't extend this pattern to network clients.
Layout
DeepDrftCli/
├── Services/
│ ├── CliService.cs # Main CLI command dispatcher
│ └── GuiService.cs # Terminal.Gui interface
├── Models/
│ └── CliSettings.cs # Config POCO (ConnectionString, VaultPath)
├── Program.cs # Entry point, DI setup
├── environment/
│ ├── connections.json # The actual config file (not appsettings.json)
│ └── config.json # Placeholder, currently unused
└── DeepDrftCli.csproj
Configuration
CliSettings (loaded from environment/connections.json):
public class CliSettings
{
public required string ConnectionString { get; set; } // SQLite path
public required string VaultPath { get; set; } // FileDatabase root
}
The config file is environment/connections.json, not appsettings.json. Example:
{
"CliSettings": {
"ConnectionString": "Data Source=../Database/deepdrft.db",
"VaultPath": "../Database/Vaults"
}
}
Paths are resolved relative to AppDomain.CurrentDomain.BaseDirectory.
DI wiring (Program.cs)
Registers:
DeepDrftContextwith SQLite (from connection string)FileDatabasesingleton (awaited on init viaFileDatabase.FromAsync)TrackRepository,DeepDrftWeb.Services.TrackService(SQL side)AudioProcessor,DeepDrftContent.Services.TrackService(content side)CliService,GuiService- Console logging
All services are scoped or singletons. The app waits for FileDatabase.FromAsync on startup (blocking), so the vault is ready before any command runs.
Mode dispatch
Program.cs checks the first argument:
guior--gui: runsGuiService.RunAsync()→ Terminal.Gui interactive interface.- Anything else: runs
CliService.RunAsync(args)→ classic CLI command parsing.
CLI commands (classic mode)
add [album] [genre] [release-date]
Positional arguments. Adds a track:
DeepDrftCli add "/path/to/song.wav" "My Song" "Artist Name" "Album Title" "Rock" "2024-01-15"
Release date format: YYYY-MM-DD. Optional: album, genre, release date.
add -i|--interactive [defaults...]
Interactive mode. Prompts for each field; command-line args become defaults:
DeepDrftCli add "/path/to/song.wav" -i "Artist Name"
Prompts: "Track Name? [default] → ", "Artist? [default] → ", etc.
list
Formats and displays all tracks from SQL:
DeepDrftCli list
Table output: ID, Name, Artist, Album, Genre, ReleaseDate, EntryKey, ImagePath.
help, --help, -h
Shows command list.
gui (or --gui)
Launches the Terminal.Gui interactive interface (separate mode).
GUI mode (Terminal.Gui)
Launches a full interactive terminal UI with:
- DeepDrft brand color scheme (Magenta/Purple/Pink).
- Track list (scrollable, selectable, deletable).
- Status pane showing feedback and errors.
- Persistent hotkey legend (F1=Help, A=Add, D=Delete, Q=Quit, etc.).
- Add dialog with file browser and track metadata entry.
- Keyboard navigation: arrow keys to navigate, Enter to select, escape to cancel.
GuiService.RunAsync() creates a Window, populates controls, and runs the main loop.
Dual-database add flow (critical contract)
This is the seam where both databases are written — must be idempotent and crash-safe:
-
DeepDrftContent.Services.TrackService.AddTrackFromWavAsync(filePath):- Validates file is
.wavand readable. - Calls
AudioProcessor.ProcessWavFileAsync(filePath)→AudioBinary(duration, bitrate extracted). - Generates a GUID entry key.
- Ensures the
tracksvault exists. - Calls
FileDatabase.RegisterResourceAsync("tracks", entryKey, audioBinary). - Returns a populated
TrackEntity(withId = 0since not yet in SQL).
- Validates file is
-
DeepDrftWeb.Services.TrackService.Create(trackEntity):- Saves the entity to SQLite.
- Returns the persisted entity with
Idassigned.
If step 1 succeeds and step 2 fails, the audio is orphaned in the vault. There is no compensating rollback today. The CLI logs both results and exits with status code indicating overall success/failure.
File validation
- Extension: must be
.wav(only WAV files are supported). - Existence: checked before processing.
- Readability: attempted during processing.
- WAV structure: validated by
AudioProcessor(RIFF/WAVE/PCM header parsing).
Non-WAV files are rejected with a clear error message.
Release date format
Only YYYY-MM-DD is accepted. Parsing is strict:
DateOnly.ParseExact(releaseDateStr, "yyyy-MM-dd")
Invalid dates result in an error. Optional fields (if not provided or invalid) are set to null.
Publishing
Build configuration in .csproj:
PublishSingleFile=true: Produces a single executable.SelfContainedandIncludeNativeLibrariesForSelfExtractare commented out — publish is framework-dependent (requires .NET runtime installed).
To publish:
dotnet publish DeepDrftCli -c Release -o ./bin/Release/publish
Result is a single .exe (Windows) or binary (Linux) that consumes the host framework.
Service registration patterns
All service calls return Result or ResultContainer<T>. CLI checks Success and Messages to display feedback:
var result = await _webTrackService.Create(trackEntity);
if (result.Success && result.Value != null)
{
Console.WriteLine($"✓ Saved to SQL with ID {result.Value.Id}");
}
else
{
Console.WriteLine($"✗ SQL save failed: {string.Join("; ", result.Messages.Select(m => m.Message))}");
}
Development commands
# Build
dotnet build DeepDrftCli
# Run classic CLI mode
dotnet run --project DeepDrftCli -- add "test.wav" "Test Track" "Test Artist"
dotnet run --project DeepDrftCli -- list
dotnet run --project DeepDrftCli -- help
# Run GUI mode
dotnet run --project DeepDrftCli -- gui
# Build for single-file publish
dotnet publish DeepDrftCli -c Release -o ./bin/Release/publish
Important patterns
- No HTTP: CLI talks directly to databases. No HTTP clients needed.
- Async/await: All database operations are async. Even CLI mode runs in an async context.
- Error swallowing in FileDatabase: Vault operations return
null/false. CLI must check return values. - Local-only: CLI has no authentication or CORS. It runs locally as an admin tool.
- Rollback: Adding a track is not transactional across the two databases. If one fails, the other may have partially succeeded (orphaned audio in the vault). Plan accordingly.
Configuration
environment/connections.json: required at runtime. Must containCliSettingswithConnectionStringandVaultPath.environment/config.json: Placeholder, currently unused. Keep it as a placeholder for future expansion.- No
appsettings.json— don't add one.
When working with this project, focus on the dual-database consistency of the add flow, CLI argument parsing, and maintaining the interactive Terminal.Gui mode as a first-class interface (not a second-class citizen).