Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b46e02d
chore(repo): bootstrap tooling, ci, and packaging
rbright Feb 19, 2026
6ca2671
feat(asr): implement local-first cli runtime and asr pipeline
rbright Feb 19, 2026
133297a
test(asr): add broad unit and integration coverage
rbright Feb 19, 2026
636489b
docs(readme): add progressive-disclosure docs set
rbright Feb 19, 2026
d030b2e
docs(config): document configurable paste shortcut
rbright Feb 19, 2026
0a0fcdb
test(ipc): stabilize read-response failure coverage
rbright Feb 19, 2026
25d4f15
fix(config): report vocabset start line on parse errors
rbright Feb 19, 2026
2f0bd1d
fix(ipc): keep probe failures distinct from running state
rbright Feb 19, 2026
eb6b585
fix(pipeline): always signal send-loop completion
rbright Feb 19, 2026
bf9540d
test(doctor): normalize riva endpoint fixture format
rbright Feb 19, 2026
523989e
docs(verification): tighten manual checklist wording
rbright Feb 19, 2026
fe5fb6d
build(codegen): use local protoc plugins for buf generate
rbright Feb 19, 2026
0cdc48d
feat(indicator): support desktop notification backend
rbright Feb 19, 2026
7d4cd9b
build(indicator): add busctl runtime dependency
rbright Feb 19, 2026
53e9468
docs(indicator): document desktop backend and placement
rbright Feb 19, 2026
c767b61
fix(config): initialize vocab map during parse
rbright Feb 19, 2026
75797b4
chore(tooling): modularize just recipes and docs
rbright Feb 19, 2026
b11ae08
docs(apps): add package and function-level code comments
rbright Feb 19, 2026
2c62d95
docs(apps): inline package docs into primary files
rbright Feb 19, 2026
d97878f
refactor(riva): split stream helpers from client lifecycle
rbright Feb 19, 2026
c32739b
docs(architecture): add FSM-derived state diagram
rbright Feb 19, 2026
4949ca7
feat(config): prefer JSONC config with legacy fallback
rbright Feb 19, 2026
c248564
fix(pipeline): harden stop paths for capture and transcriber reuse
rbright Feb 19, 2026
206face
fix(riva): bound stream startup and cancel stalled init
rbright Feb 19, 2026
6d8880f
fix(pipeline): avoid startup deadlock on stream init failure
rbright Feb 19, 2026
0640ec2
chore(nix): use hostPlatform system for module package wiring
rbright Feb 19, 2026
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
19 changes: 19 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

[*.go]
indent_style = tab
indent_size = 4

[Makefile]
indent_style = tab

[*.md]
trim_trailing_whitespace = false
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI

on:
push:
branches:
- main
pull_request:

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Nix
uses: cachix/install-nix-action@v27
with:
extra_nix_config: |
experimental-features = nix-command flakes

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.work

- name: Install just
uses: taiki-e/install-action@just

- name: Nix lint
run: just lint-nix

- name: Pre-commit hooks
run: just precommit-run

- name: Guardrails
run: just ci-check

- name: Nix build package
run: nix build 'path:.#sotto'

- name: Nix run --help smoke
run: nix run 'path:.#sotto' -- --help
37 changes: 37 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Nix
uses: cachix/install-nix-action@v27
with:
extra_nix_config: |
experimental-features = nix-command flakes

- name: Build
run: nix build 'path:.#sotto'

- name: Package binary
run: |
mkdir -p dist
cp result/bin/sotto dist/sotto
chmod +x dist/sotto

- name: Create release
uses: softprops/action-gh-release@v2
with:
files: dist/sotto
generate_release_notes: true
27 changes: 27 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Build artifacts
/bin/
/dist/
/result
/result-*
/apps/sotto/sotto
/apps/sotto/vendor/

# Go
**/*.test
**/*.out
coverage.out

# Editor/system
.DS_Store
.direnv/
.env
.env.*

# Runtime artifacts
*.log
*.sock

# Local planning/session notes
.pi/
PLAN.md
SESSION.md
26 changes: 26 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
run:
timeout: 5m
tests: true

linters:
disable-all: true
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- typecheck
- unused
- revive
- misspell

linters-settings:
revive:
rules:
- name: package-comments
disabled: true

issues:
max-issues-per-linter: 0
max-same-issues: 0
11 changes: 11 additions & 0 deletions .just/ci.just
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ci:
just fmt
just lint
just test

ci-check:
just fmt-check
just lint
just test
just generate
git diff --exit-code -- apps/sotto/proto/gen/go
2 changes: 2 additions & 0 deletions .just/codegen.just
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
generate: tools
PATH="{{bin_dir}}:$PATH" "{{bin_dir}}/buf" generate apps/sotto/proto/third_party --template buf.gen.yaml
5 changes: 5 additions & 0 deletions .just/common.just
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
bin_dir := justfile_directory() + "/bin"
tooling_flake := "path:" + justfile_directory()

default:
@just --list
24 changes: 24 additions & 0 deletions .just/go.just
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Install pinned developer tools into ./bin.
tools:
mkdir -p "{{bin_dir}}"
GOBIN="{{bin_dir}}" go install github.com/bufbuild/buf/cmd/buf@v1.57.2
GOBIN="{{bin_dir}}" go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.6
GOBIN="{{bin_dir}}" go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1
GOBIN="{{bin_dir}}" go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.8

# Format Go sources (excluding generated vendor paths).
fmt:
bash -euo pipefail -c 'mapfile -t files < <(find apps/sotto -type f -name "*.go" -not -path "*/vendor/*"); if [[ "${#files[@]}" -eq 0 ]]; then exit 0; fi; gofmt -w "${files[@]}"'

# Check Go formatting only (no changes).
fmt-check:
bash -euo pipefail -c 'mapfile -t files < <(find apps/sotto -type f -name "*.go" -not -path "*/vendor/*"); if [[ "${#files[@]}" -eq 0 ]]; then exit 0; fi; test -z "$(gofmt -l "${files[@]}")"'

lint: tools
"{{bin_dir}}/golangci-lint" run ./apps/sotto/...

test:
go test ./apps/sotto/...

test-integration:
go test -tags=integration ./apps/sotto/internal/audio -run Integration
8 changes: 8 additions & 0 deletions .just/hooks.just
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
precommit-install:
nix develop '{{ tooling_flake }}' -c prek install --hook-type pre-commit --hook-type pre-push

precommit-run:
nix develop '{{ tooling_flake }}' -c prek run --all-files --hook-stage pre-commit

prepush-run:
nix develop '{{ tooling_flake }}' -c prek run --all-files --hook-stage pre-push
13 changes: 13 additions & 0 deletions .just/nix.just
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
nix-build-check:
nix build 'path:.#sotto'

nix-run-help-check:
nix run 'path:.#sotto' -- --help

fmt-nix:
nix develop '{{ tooling_flake }}' -c nixfmt flake.nix

lint-nix:
nix develop '{{ tooling_flake }}' -c deadnix flake.nix
nix develop '{{ tooling_flake }}' -c statix check flake.nix
nix develop '{{ tooling_flake }}' -c nixfmt --check flake.nix
11 changes: 11 additions & 0 deletions .just/smoke.just
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
smoke-riva-doctor:
sotto doctor

smoke-riva-manual:
@echo "Run this in an active Hyprland session with local Riva up:"
@echo " 1) sotto doctor"
@echo " 2) sotto toggle # start recording"
@echo " 3) speak a short phrase"
@echo " 4) sotto toggle # stop+commit"
@echo " 5) verify clipboard/paste + cues"
@echo " 6) sotto cancel # verify cancel path"
57 changes: 57 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
exclude: ^apps/sotto/(vendor/|proto/third_party/)
stages: [pre-commit]
- id: end-of-file-fixer
exclude: ^apps/sotto/(vendor/|proto/third_party/)
stages: [pre-commit]
- id: check-added-large-files
exclude: ^apps/sotto/(vendor/|proto/third_party/)
stages: [pre-commit]
- id: check-merge-conflict
stages: [pre-commit]

- repo: local
hooks:
- id: go-fmt-check
name: go fmt check
entry: just fmt-check
language: system
pass_filenames: false
always_run: true
stages: [pre-commit]

- id: lint-nix
name: nix lint
entry: just lint-nix
language: system
pass_filenames: false
always_run: true
stages: [pre-push]

- id: ci-check
name: ci-check
entry: just ci-check
language: system
pass_filenames: false
always_run: true
stages: [pre-push]

- id: nix-build-check
name: nix build package
entry: just nix-build-check
language: system
pass_filenames: false
always_run: true
stages: [pre-push]

- id: nix-run-help-check
name: nix run --help smoke
entry: just nix-run-help-check
language: system
pass_filenames: false
always_run: true
stages: [pre-push]
95 changes: 95 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# sotto Agent Guide

## Scope

Applies to the entire `sotto/` repository.

## Mission

Deliver a production-grade, local-first ASR CLI:

- single Go binary
- no daemon/background service
- deterministic toggle/stop/cancel behavior
- strong cleanup and failure safety
- reproducible tooling + packaging

## Fast Start (read in order)

1. `README.md` (user-facing behavior)
2. `PLAN.md` (active checklist)
3. `SESSION.md` (what was actually executed)
4. `just --list` (task entrypoints)
5. Only then open the package(s) you need to change

## Component Map

| Task area | Primary paths |
| --- | --- |
| CLI + dispatch | `apps/sotto/internal/cli/`, `apps/sotto/internal/app/` |
| IPC + single-instance | `apps/sotto/internal/ipc/` |
| Session/FSM | `apps/sotto/internal/session/`, `apps/sotto/internal/fsm/` |
| Audio capture | `apps/sotto/internal/audio/` |
| Riva streaming | `apps/sotto/internal/riva/`, `apps/sotto/internal/pipeline/` |
| Transcript assembly | `apps/sotto/internal/transcript/` |
| Clipboard/paste output | `apps/sotto/internal/output/`, `apps/sotto/internal/hypr/` |
| Indicator + cues | `apps/sotto/internal/indicator/` |
| Config system | `apps/sotto/internal/config/` |
| Diagnostics/logging | `apps/sotto/internal/doctor/`, `apps/sotto/internal/logging/` |
| Tooling/packaging | `justfile`, `.just/`, `flake.nix`, `.github/workflows/` |
| Proto/codegen | `apps/sotto/proto/third_party/`, `apps/sotto/proto/gen/`, `buf.gen.yaml` |

## Non-Negotiable Workflow Rules

1. Read target files before editing.
2. Keep scope tight to the requested behavior.
3. Update `PLAN.md` checklist items only when executed + verified.
4. Log key decisions and commands in `SESSION.md`.
5. Add or update regression tests for behavior changes when feasible.
6. Do not claim runtime verification unless it was actually run.

## Go Engineering Standards

Write canonical, idiomatic Go:

- `gofmt` clean, straightforward naming, small focused functions
- explicit constructors and dependency wiring (no hidden globals)
- `context.Context` first for cancelable/timeout-aware operations
- wrap errors with actionable context; use `errors.Is` for branching
- keep interfaces near consumers; avoid broad shared interfaces
- separate state/policy logic from I/O adapters
- avoid clever abstractions; prefer explicit control flow

## Testing Policy

- Use `testing` + `testify` (`require`/`assert`) as needed.
- Prefer real boundaries/resources (temp files, unix sockets, `httptest`, PATH fixtures).
- Do **not** introduce mocking frameworks.
- Riva runtime inference remains manual smoke (non-CI).

## Config Change Contract (Mandatory)

Any config-key change must update all relevant locations:

1. `internal/config/types.go`
2. `internal/config/defaults.go`
3. `internal/config/parser.go`
4. `internal/config/validate.go` (if constraints change)
5. parser/validation tests
6. `docs/configuration.md` and any README examples
7. consuming defaults in external config repos when in scope

## Required Checks Before Hand-off

Run and report:

1. `just ci-check`
2. `nix build 'path:.#sotto'`

If skipped, state exactly what was skipped, why, and how to run it.

## Safety

- Never commit secrets (e.g., `NGC_API_KEY`).
- Avoid destructive shell operations unless explicitly requested.
- Do not edit outside `sotto/` unless explicitly asked.
Loading
Loading