Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file orients coding agents and future maintainers. It is not an end-user ma

## Purpose

**PrimeDictate** is a .NET 8 **Windows** app with a **WPF tray shell** and **first-run onboarding**. It:
**PrimeDictate** is a .NET 10 **Windows** app with a **WPF tray shell** and **first-run onboarding**. It is what the installers ship (6.0.0 and later). A cross-platform successor lives under `src/` (see the layout table) and is not packaged yet. The WPF app:

1. Listens for a **global** hotkey (`Ctrl+Shift+Space` / SharpHook) to start and stop capture.
2. Records from the **default** Windows input device using **WASAPI** (NAudio `WasapiCapture`), normalizing to **16 kHz, 16-bit, mono** PCM.
Expand All @@ -24,11 +24,16 @@ This file orients coding agents and future maintainers. It is not an end-user ma
| `WhisperTextInjectionPipeline.cs` | Transcription orchestration, logging, and final-only text injection. |
| `WindowsInputHelpers.cs` | Foreground-window guard for final injection and optional Windows Mouse Sonar pulse. |
| `TranscriptionOverlayWindow.xaml` | Non-activating live transcript overlay; placement is user-configurable. |
| `PrimeDictate.csproj` | Target `net8.0`; NAudio, SharpHook, sherpa-onnx. |
| `PrimeDictate.csproj` | The WPF app. Target `net10.0-windows` (SDK pinned in `global.json`); NAudio, SharpHook, sherpa-onnx; references `src/PrimeDictate.Core`. |
| `Directory.Build.props` | Shared assembly/file `Version` (installers read this too). |
| `scripts/Publish-Windows.ps1` | Self-contained `win-x64` publish to `artifacts/win-x64/publish`. |
| `scripts/Build-Installers.ps1` | Publishes then builds WiX `.wixproj` targets (NuGet `WixToolset.Sdk`) to MSIs in `artifacts/installer`. |
| `installer/wix/` | WiX: offline MSI bundles the default Whisper ONNX files + Start Menu shortcut; online MSI uses `WixQuietExec` to run `DownloadModel.cmd`; `Branding.wxs` + `PrimeDictate.ico` for ARP/exe icon. |
| `scripts/Publish-Windows.ps1` | Self-contained publish of the WPF app for one runtime (`win-x64` or `win-arm64`) to `artifacts/<rid>/publish`. |
| `scripts/Build-Installers.ps1` | Publishes then builds the online WiX `.wixproj` (NuGet `WixToolset.Sdk`) to x64 and ARM64 MSIs in `artifacts/installer`. |
| `installer/wix/` | WiX: `online/` is the shipped MSI (app payload only; models are downloaded in the app), with Start Menu and optional launch-at-login shortcuts; `offline/` is not built. `Branding.wxs` + `PrimeDictate.ico` for ARP/exe icon. |
| `src/PrimeDictate.Core` | Portable core used by both apps: transcript model, SQLite session store, pipeline, dictation controller, audio and provider contracts. |
| `src/PrimeDictate.Platforms` | Platform adapters: microphone capture (miniaudio; WASAPI loopback for system audio), speech providers (sherpa-onnx, Nemotron), hotkeys, typing, the meeting final pass. |
| `src/PrimeDictate.Desktop` | The cross-platform Avalonia app: transcription workspace, meetings, dictation. Not in the installers yet. |
| `tests/PrimeDictate.Core.Tests` | xUnit tests for Core and Platforms: `dotnet test tests/PrimeDictate.Core.Tests`. |
| `docs/architecture/` | Design notes and status per stage; `meeting-v1-acceptance.md` is the Meeting v1 test. |

## Conventions to preserve

Expand All @@ -45,6 +50,7 @@ This file orients coding agents and future maintainers. It is not an end-user ma
- **org.k2fsa.sherpa.onnx** for ONNX speech recognition runtimes and managed bindings.
- **NAudio** for capture and resampling.
- **SharpHook** for the global hook and `EventSimulator`.
- Under `src/`: **Avalonia** (the new app), **SoundFlow** (miniaudio capture) and **Microsoft.Data.Sqlite** (session store and dictation history).

**TextCopy** is not used; do not add it back unless you implement a clipboard strategy that is demonstrably free of the paste/restore race.

Expand All @@ -62,16 +68,22 @@ The tray/onboarding milestone is now implemented:
1. **Host process**: WPF tray host (`App.xaml`) with notification icon, Settings/Exit menu, live transcript overlay, and Ready/Listening/Processing tooltip state.
2. **User settings**: Persisted under `%LocalAppData%\PrimeDictate\settings.json`; loaded at startup and applied to `GlobalHotkeyListener`.
3. **First run**: Missing/incomplete settings show `SettingsWindow` before normal tray-only behavior.
4. **WiX launch option**: Offline and online packages include finish-page launch checkbox to start `PrimeDictate.exe`.
4. **Installers**: online MSIs only (x64, ARM64). They do not launch the app from the finish page; launch at login is on by default and `LAUNCHATLOGIN=0` turns it off.
5. **Preserved invariants**: hook-thread offload, foreground-window guard, overlay-only live preview, ONNX model-folder validation, and final-only target `SimulateTextEntry` baseline remain intact.

## Build and test hints

- **Windows installers**: See [installer/README.md](installer/README.md). Offline MSIs require `models\whisper\sherpa-onnx-whisper-base.en\base.en-encoder.int8.onnx`, `base.en-decoder.int8.onnx`, and `base.en-tokens.txt` on the maintainer machine. Online MSIs download and extract that same ONNX model after install.
- **Windows installers**: See [installer/README.md](installer/README.md). CI builds the online x64 and ARM64 MSIs; users download a model in the app's first-run setup or Settings.
- Run from the **repository root** so `./models/whisper/<model folder>` can be discovered during development when models are staged in the repo-local `models` tree.
- A running `PrimeDictate.exe` from `dotnet run` can **lock the apphost**; stop the process if `MSB3021` / copy-to-output fails.
- Linter: project should build with **0 warnings** under default SDK analysis when possible; platform-specific API use should stay behind `OperatingSystem` checks or documented trade-offs.

## Releases

- Merging into `main` publishes nothing. A release is a "Release X.Y.Z" commit on `main` that bumps `Directory.Build.props` (`Version`, `AssemblyVersion`, `FileVersion`), then a pushed `vX.Y.Z` tag.
- The tag run of `.github/workflows/build.yml` builds and signs the WPF app, publishes the GitHub Release (x64 and ARM64 MSIs with checksums), pushes the Chocolatey package (then Chocolatey moderation) and opens a winget-pkgs PR (then winget validation and moderation). Details and fixes: [installer/README.md](installer/README.md).
- Never merge to `main`, tag a release or change signing without the owner's explicit go-ahead.

## Out of scope (unless explicitly requested)

- Cloud APIs, always-on online STT, or shipping large model blobs inside the repository; document/download them instead.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,9 @@ For installer-specific details, see `installer/README.md`.

## Release and Distribution

Tagged pushes that match `vX.Y.Z` build the app, produce release assets, and publish to GitHub Releases.
Tagged pushes that match `vX.Y.Z` build the app, produce release assets, and publish to GitHub Releases. Merging into `main` alone publishes nothing.

The release pipeline packages the Windows WPF app (`PrimeDictate.exe`). The cross-platform app under `src/` is in the repository but not in the installers yet.

In practice, the release pipeline keeps three public distribution channels aligned:

Expand All @@ -360,7 +362,7 @@ If you only want to install or evaluate PrimeDictate, you can stop reading here.
- Direct x64 MSI asset: `https://github.com/flowdevs-io/PrimeDictate/releases/download/vX.Y.Z/PrimeDictate-Setup-vX.Y.Z-x64.msi`
- Direct ARM64 MSI asset: `https://github.com/flowdevs-io/PrimeDictate/releases/download/vX.Y.Z/PrimeDictate-Setup-vX.Y.Z-arm64.msi`

If signing secrets are unavailable, the release flow still publishes unsigned assets instead of failing before upload.
If signing secrets are unavailable, the release flow still publishes unsigned assets instead of failing before upload. If they are set but signing fails (for example an expired secret), the run stops before anything is published.

### winget

Expand All @@ -374,6 +376,8 @@ For resubmission without rebuilding binaries, use `workflow_dispatch` with:
- `submit_winget_only=true`
- `target_version=X.Y.Z`

The winget step never fails the release, so check its log after each tag run. If it could not sync the submitting fork, see "When the winget step warns" in `installer/README.md`.

### Chocolatey

- Package id: `primedictate`
Expand Down
18 changes: 18 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# PrimeDictate 6.0.0

## Highlights

- Runs on .NET 10. The x64 and ARM64 installers stay self-contained and signed, so nothing extra needs installing.
- Wake word: "ok" and "okay" are treated the same, and "thanks" / "thank you" variants are recognized.
- Voice commands: more spoken variants of a command phrase are removed from the typed text.
- New particle visualizer in the overlay and a redrawn tray icon.
- `--show` and `--workspace` open the workspace window.
- Under the hood the app now builds on the portable PrimeDictate core, shared with the upcoming cross-platform app. That app is in the repository but not in these installers yet.

# PrimeDictate 5.1.0

## Highlights

- Added optional wake-word dictation start.
- Polished the dark UI shells.

# PrimeDictate 5.0.0

## Highlights
Expand Down
2 changes: 2 additions & 0 deletions docs/architecture/stage-7-dictation-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Branch `claude/dictation-parity-4k0tl9`, following `stage-7-parity-plan.md`. "Ran" means executed on Linux in a container; "Compiled only" means it builds but has not been run on that OS.

**Status (2026-09-30):** merged into `main` (PR #3). Release 6.0.0 ships the WPF app; this app is not in the installers yet. Hotkey and wake-word dictation, the overlay and the settings window were first run on Windows the same day (see the Windows notes below). Before it can replace the WPF app it still needs Whisper.net models (the WPF backend Justin uses), an updater and its own installer.

## Done

| Piece | Where | Verified |
Expand Down
29 changes: 22 additions & 7 deletions installer/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Windows installers (WiX / MSI)

Installers are native **x64** and **ARM64** Windows Installer packages (`.msi`) built with the [WiX Toolset](https://wixtoolset.org/) **through NuGet** (`WixToolset.Sdk`). You only need the **.NET 8 SDK**; you do **not** install WiX separately.
Installers are native **x64** and **ARM64** Windows Installer packages (`.msi`) built with the [WiX Toolset](https://wixtoolset.org/) **through NuGet** (`WixToolset.Sdk`). You only need the **.NET 10 SDK** (pinned in `global.json`); you do **not** install WiX separately.

The installers package the Windows WPF app (`PrimeDictate.csproj`, `PrimeDictate.exe`). The cross-platform app under `src/` is not in them yet.

The **online** MSI installs the PrimeDictate app payload only. Model acquisition happens inside PrimeDictate's first-run setup and Settings window.

Expand Down Expand Up @@ -45,20 +47,33 @@ PrimeDictate publishes to winget from the same tagged release pipeline as the MS

### Maintainer flow (recommended)

1. Ensure `Directory.Build.props` has the intended release version (for example `3.2.0`).
2. Push release commit to `main`.
3. Create and push tag `v<version>` (for example `v3.2.0`).
1. Bump `Directory.Build.props` to the release version (`Version`, `AssemblyVersion`, `FileVersion`; for example `6.0.0`) and add the version to `RELEASE_NOTES.md`.
2. Push the "Release <version>" commit to `main`. Merging into `main` publishes nothing by itself.
3. Create and push tag `v<version>` (for example `v6.0.0`).
4. The `build.yml` tag run will:
- build x64 and ARM64 publish payloads + online MSIs,
- build x64 and ARM64 publish payloads + online MSIs, signed when the Azure Key Vault secrets are set,
- attach both MSIs and checksum files to the matching GitHub Release,
- pack and push the Chocolatey package (it then waits in Chocolatey moderation),
- generate and validate winget manifests from those MSI artifacts,
- submit a winget PR when `WINGET_CREATE_GITHUB_TOKEN` is configured.
- submit a winget PR when `WINGET_CREATE_GITHUB_TOKEN` is configured (it then goes through winget validation and moderation).

If `WINGET_CREATE_GITHUB_TOKEN` is missing, the workflow still builds assets and publishes to GitHub Releases, but skips winget submission.

The winget and Chocolatey steps never fail the release, so a green run can still hide a warning: check both steps' logs after every release.

### When the winget step warns

`The forked repository could not be synced with the upstream commits` means the `winget-pkgs` fork that the token submits from (`CakeRepository/winget-pkgs`) has fallen behind and the token cannot sync it, typically because upstream changed workflow files, which needs the `workflow` scope. Sync the fork with an account that has that scope, then resubmit without rebuilding (below):

```powershell
gh repo sync CakeRepository/winget-pkgs --source microsoft/winget-pkgs --branch master
```

This happened for 6.0.0: the fork was 27,575 commits behind.

### winget resubmission (no rebuild)

Use this when winget reviewers request metadata changes for an existing version.
Use this when winget reviewers request metadata changes for an existing version, or when the tag run's winget step warned.

1. Run `build.yml` with `workflow_dispatch`.
2. Set `submit_winget_only=true`.
Expand Down
Loading