You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Linux recording support for journal log. The mic-recording toggle
(bare journal log) now works on Linux, not just macOS: it captures via ffmpeg -f pulse by default, or -f alsa on ALSA-only boxes (set log.audio.backend: alsa), selected automatically by OS
(log.audio.backend: "", the default) with an explicit override available.
Desktop notifications on Linux go through notify-send, degrading silently
(same as macOS's osascript/terminal-notifier) when it's absent. An
unsupported platform (anything other than macOS/Linux) fails fast on the
starting press, before the recording daemon spawns. macOS behavior
(avfoundation, osascript/terminal-notifier) is unchanged.
log.audio.{silence_duration,silence_noise_db} config keys. The silence_autostop watchdog's silence-interval length (default 30 seconds)
and silencedetect noise floor (default -35 dB) are now configurable
instead of hardcoded, so a noisy room or a different mic can tune when the
safety-net stop fires. Existing behavior is unchanged unless overridden.
Meeting pipeline migrated onto the shared land/index core (Phase 5c). journal quill-sync and journal transcribe now write and index through the
same internal/log.Land/IndexTranscript primitives the voice-note pipeline
(journal log) uses, replacing bespoke os.WriteFile glue in each command.
Frontmatter (source: quill/source: whisperx), filenames, and chunking
behavior are byte-identical to before — this is an internal refactor, not a
behavior change. journal models pull gains a second, independently-optional
gated model slot (diarization.*, same shape as transcriber.*) so the
meeting pipeline's pyannote diarization model can be provisioned as a fast
credential preflight — see docs/TRANSCRIBE.md and docs/CONFIGURATION.md. diarization.model_id is
empty (disabled) by default; journal models pull skips it entirely until
configured, and attempts every configured model even if one fails, exiting
non-zero only if any of them did. internal/models.Pull gained a PullFile variant supporting a configurable remote/local filename (needed
since pyannote's repo has no model.bin) — Pull is now a thin wrapper
around it with no behavior change for existing whisper-model call sites.
journal models pull gated/HuggingFace-token path (Phase 5b). Extends
Phase 2a's model provisioning with support for gated HuggingFace repos (the
first being pyannote's speaker-diarization models, arriving with the meeting
pipeline migration). Set transcriber.gated: true and transcriber.accept_url
in config; pull sends HF_TOKEN (read from the environment only, never
config) as a Bearer token. A gated model with no/invalid token fails with an
explicit "accept terms at <url>, set HF_TOKEN" message instead of a raw
401. MODELS.md now records each installed model's gated status and
acceptance-page link. Ungated pulls are unaffected — no token, no behavior
change.
MCP tools journal_log_text / journal_log_audio (Phase 5a). The MCP server
(journal mcp) now exposes the journal log --text and journal log <audio.wav>
pipelines as tools: journal_log_text(text) runs shape→assemble→land→index, journal_log_audio(audio_path) runs transcribe→shape→assemble→land→index for a
server-local audio file. Both return {path, title, landed} and honor local_only/local_only_mcp exactly like the rest of the MCP surface. The
mic-recording stage stays CLI-only and is never exposed over MCP — an MCP server
must not seize the user's microphone.
Start/finish desktop notifications for journal log (Phase 4, macOS only).
The recording toggle now pairs the existing terminal output with a real desktop
notification: starting a recording pops "● recording", and the async pipeline
landing the note pops "✓ logged: <title>" with the note's relative path. Sent
via osascript (display notification), falling back to terminal-notifier if
osascript is unavailable; a missing/failing notifier degrades silently (logged,
not surfaced) and never blocks or fails the recording or pipeline.
internal/audio.Notifier: an injectable desktop-notification boundary
(DefaultNotifier for production, FakeNotifier for tests) mirroring the
existing Recorder pattern, so tests never pop a real OS notification.
Hammerspoon hotkey binding documented (docs/USAGE.md) — an init.lua
snippet binding a single hotkey to bare journal log, plus an optional
menubar-dot add-on, so the recording toggle is fully usable from one key press.
journal log recording toggle — mic capture (Phase 3, macOS only). The bare journal log command now toggles mic recording: the first press starts a detached
background recorder (ffmpeg -f avfoundation, 16 kHz/mono/16-bit PCM) and prints
"● recording"; the second press stops it and hands the finalized WAV off to the
existing transcribe→shape→assemble→land→index pipeline asynchronously, so both
presses return immediately. --start/--stop/--cancel/--status give explicit
control (--cancel discards the recording — no note is produced). The toggle state
lives in a lockfile ($XDG_RUNTIME_DIR/journal-log.lock or <tmp>/journal-log/journal-log.lock) holding {pid, wav_path, started_at}; a dead
PID is detected and cleaned up automatically on the next press. log.audio.max_duration
(default 900s) self-finalizes a long recording; log.audio.silence_autostop (default
off) is an optional safety-net stop after sustained silence. The recorded WAV is
deleted after a successful run unless log.audio.keep_wav: true, in which case its
path is recorded in the landed note's audio: frontmatter field. A WAV passed
directly (journal log <file>.wav) is never auto-deleted, regardless of this
setting — only recorder-produced scratch files are.
internal/audio package: lockfile primitives (ReadLock/WriteLock/RemoveLock,
injectable PIDAlive) and an injectable Recorder interface (FfmpegRecorder for
production, FakeRecorder for tests) so the mic/ffmpeg dependency never runs in tests.
log.audio.{tmp_dir,max_duration,silence_autostop,keep_wav} config keys and the LogAudioTmpDirAbs() config accessor, additive to the existing log.audio.{device, sample_rate,channels} keys.
journal log <audio.wav> — transcribe stage (Phase 2b). Pass a WAV file as
a positional argument to transcribe it locally via whisper.cpp and run the full
transcribe→shape→assemble→land→index pipeline. No network access: a missing model
fails fast with "run journal models pull". Silent recordings skip the pipeline;
transcription errors are retryable (WAV is kept). The transcriber frontmatter
records the backend/model used; duration_sec is derived from the WAV header.
log.transcriber.{backend,model,model_dir} config keys replace the previous
stub log.transcriber.{engine,model} keys. backend selects the engine
(whisper.cpp, the only built-in); model_dir defaults to the same path as transcriber.model_dir so a single journal models pull serves both paths.
LogTranscriberModelDirAbs() helper on *config.Config mirrors TranscriberModelDirAbs() for the log transcriber path.
internal/log.Transcriber interface with a FakeTranscriber for tests and a WhisperCPP default implementation. The boundary is injectable so commands never
exec a real binary in tests.
journal log --text "..." — voice note capture (Phase 1). The full
shape→assemble→land→index pipeline is now available for typed text. The LLM
shaping step (configurable via log.shaping.enabled) cleans disfluencies,
generates a title and summary, extracts @todo/@decision/@question markers,
and tags the note. Notes always land to logs/YYYY-MM-DD-HHMM-<slug>.md even
when shaping is unavailable (raw fallback). Index failure is non-fatal.
SourceVoice = "voice" source constant in internal/store; voice chunks
are indexed separately from notes and transcripts. No schema migration required —
the source TEXT column already accepts any value.
log: config namespace with shaping, landing, audio, and transcriber
keys; LogAbsPath()/LogRelPath() helpers mirror TranscriptsAbsPath().
Fixed
journal log hotkey/mic-toggle no longer fails silently. When the
background transcribe→land pipeline can't run (most commonly whisper.cpp or the
model isn't installed), the detached pipeline process has no visible stdout, so
the only feedback the user gets is a desktop notification — and previously only
the success notification (✓ logged) ever fired. journal log now pops a ✕ journal log failed notification naming the retained WAV to retry with, and a
low-key "empty recording — nothing to log" notification when a recording
transcribes to silence, so a hotkey press always resolves to a visible outcome.