talkis/AGENTS.md
David Perov e30ee9c25e feat: home stats, processing stop/retry, uniform hotkey editing, support link
- Words statistics on the home page (month / all-time / words-per-minute), minimalist layout
- Live processing row in history with stop/cancel; retry for interrupted & failed entries (voice/file/call); startup reconciliation of orphaned jobs
- Uniform hotkey editing across OSes: physical-key (event.code) capture + apply-on-keydown on macOS
- Contact-support button and a GitHub link next to the version
- Stop ducking other apps audio: check mic permission via the Permissions API instead of opening getUserMedia at startup
- CI workflow running cargo check on macOS/Windows/Linux on push and PR
2026-06-22 00:24:40 +03:00

14 KiB

Talkis - AGENTS.md

Talkis is a macOS voice-to-text application built with Tauri v2 (Rust backend) and React (TypeScript frontend), with a companion cloud platform (Next.js).

Project Rules

All reusable project rules are connected from this root AGENTS.md.

  • For website or landing-page style work, follow rules/style.rule.md.
  • For reusable Talkis-inspired product UI work in this repo or other apps, use skills/talkis-product-ui-style/; keep it aligned with rules/style.rule.md.
  • For release work, follow rules/release.rule.md.
  • Keep skills/ for reusable Codex skills that may be installed outside this repo.
  • Keep site/ for actual website files only; do not store agent rules there.
  • Keep docs/release/ for release review artifacts and templates only; do not store the release workflow rule there.

Project Structure

talkis/
├── src/                      # Frontend (React/TypeScript)
│   ├── windows/
│   │   ├── widget/           # Small floating widget window
│   │   └── settings/         # Settings window with tabs
│   ├── components/           # Shared React components
│   ├── lib/
│   │   ├── store.ts          # Persistent settings (tauri-plugin-store)
│   │   ├── logger.ts         # Logging utilities
│   │   ├── permissions.ts    # OS permission checks
│   │   ├── cloudAuth.ts      # Cloud auth client (talkis.ru API)
│   │   └── utils.ts          # Helper functions
│   └── main.tsx              # Entry point (routes to widget/settings)
├── src-tauri/
│   ├── src/
│   │   ├── lib.rs            # Tauri commands, window management
│   │   ├── ai.rs             # Whisper + LLM API calls
│   │   ├── paste.rs          # Clipboard paste simulation
│   │   └── logger.rs         # File logging
│   └── Cargo.toml
├── rules/                   # Agent-facing project rules
│   ├── style.rule.md        # Website style rule
│   └── release.rule.md      # Release workflow rule
├── skills/                  # Reusable Codex skills sourced from this repo
│   └── talkis-product-ui-style/ # Portable Talkis product UI style skill
├── site/                    # Static website files
├── talkis-web/              # Cloud platform (Next.js 15)
│   ├── src/app/              # Pages: landing, auth, dashboard
│   ├── src/components/       # Landing, dashboard, shared components
│   ├── src/lib/              # Auth, Prisma, email
│   ├── prisma/schema.prisma  # DB schema (7 models)
│   └── .env.example          # Environment variables template
└── package.json

Build Commands

# Development (hot reload)
bun run tauri dev

# Build for production
bun run tauri build

# Build signed local macOS release artifact
bun run build:release:macos

# TypeScript check
bunx tsc --noEmit

# Rust check
cd src-tauri && cargo check

# Release checks
bun run check:release

# View logs
bun run logs          # tail -f ~/.talkis/talkis.log
bun run logs:clear    # rm ~/.talkis/talkis.log

# ── talkis-web ──
cd talkis-web && bun run dev       # Next.js dev server
cd talkis-web && bunx tsc --noEmit # TS check
cd talkis-web && bunx prisma migrate dev --name <name>  # DB migration

Design System

Fonts

Token Font Usage
--font / --font-main Inter Body text, UI elements
--font-accent Manrope 800 All headings — bold, sans-serif, letter-spacing: -0.04em
--font-brand Manrope 800 Logo wordmark, letter-spacing: -0.06em, uppercase

Rule: Headings are NEVER italic. Both the Tauri app and web use Manrope for headings — not Playfair Display.

Color Palette (Cappuccino Theme)

Token Value Usage
--bg / --bg-cappuccino #faf9f6 Page background
--text-hi #000000 Primary text
--text-mid #39342d / #666 Secondary text
--text-low #5d564d / #999 Tertiary / hint text
--border rgba(0,0,0,0.09) Subtle borders

Interactive Elements Style

Nav items, cards, and interactive elements follow a soft style:

  • Border radius: 10px for nav items, cards, buttons in sidebar
  • Active state: background: rgba(0,0,0,0.04) + font-weight: 600 + color: var(--text-hi) — never inverted black
  • Hover: background: rgba(0,0,0,0.04)
  • Icons: size={18}, strokeWidth active 2.2, inactive 1.6

Buttons

Class Style Usage
btn-black Black bg, white text, uppercase, rounded-full Primary CTA
btn-outline Transparent, black border, uppercase, rounded-full Secondary CTA

CTA Subscription Block

The sidebar CTA is a light card (not inverted black):

  • background: rgba(0,0,0,0.03), border: 1px solid rgba(0,0,0,0.06)
  • Dark text, dark icons
  • Button inside is btn-black style (black bg, white text)

Code Style

TypeScript/React

Imports: Group by external → internal, use explicit file extensions for clarity when needed.

// External
import { useEffect, useState } from "react";
import { invoke } from "@tauri-apps/api/core";

// Internal - use relative paths
import { getSettings } from "../../lib/store";
import { logInfo } from "../../lib/logger";

Components: Use function components with explicit return types.

export function MyComponent({ prop }: { prop: string }): JSX.Element {
  // hooks at the top
  const [state, setState] = useState<string>("");
  
  // early returns for loading/error states
  if (!state) return null;
  
  // main render
  return <div>{prop}</div>;
}

Styles: Use inline styles with CSS variables. No CSS modules or styled-components.

// Good
<div style={{
  display: "flex",
  padding: "16px 20px",
  background: "var(--surface)",
}}>

Types: Prefer explicit interfaces over type aliases. Use union types for finite states.

type WidgetState = "idle" | "recording" | "processing" | "error";

interface AppSettings {
  apiKey: string;
  hotkey: string;
  // ...
}

Error handling: Always handle errors gracefully, show user-friendly messages.

try {
  await someAsyncOperation();
} catch (e) {
  const msg = e instanceof Error ? e.message : "Unknown error";
  showError(`Операция не удалась: ${msg}`);
}

Logging: Use the logger utility for important events.

import { logInfo, logError } from "../../lib/logger";

logInfo("HOTKEY", "Registered successfully");
logError("API", `Failed: ${e}`);

Rust

Commands: Use #[tauri::command] with async when needed.

#[tauri::command]
pub async fn my_command(param: String) -> Result<MyResponse, String> {
    // implementation
}

Error handling: Use Result<T, String> for commands, convert errors with .map_err(|e| e.to_string()).

let result = some_operation()
    .map_err(|e| format!("Operation failed: {}", e))?;

Logging: Use the logger module.

logger::log_info("TAG", "message");
logger::log_error("TAG", &format!("error: {}", e));

Architecture Notes

  • Two windows: Widget (50x18px floating) and Settings (separate window)
  • Global shortcuts: Use tauri-plugin-global-shortcut for hotkey registration
  • Persistent storage: Use tauri-plugin-store with JSON file
  • Permissions: Check microphone via getUserMedia(), accessibility via system dialog
  • API calls: Whisper for transcription, GPT-4o-mini for text cleanup
  • Cloud platform: talkis-web/ — Next.js 15, Auth.js v5, Prisma, PostgreSQL
  • Auth flow: Email OTP + Yandex OAuth → deep link talkis://auth?token=xxx
  • Subscription: Free (own API key) or paid (cloud, 390₽/mo)

Audio Pipeline Rules

For any audio, transcription, local STT, file transcription, or call-capture work, read docs/audio-pipeline-principles.md before editing code.

Key source files:

  • Voice widget recording: src/windows/widget/hooks/useWidgetRecording.ts, src/windows/widget/services/recordingRuntime.ts
  • Native voice recording: src-tauri/src/native_voice_recorder.rs
  • STT request orchestration and hallucination filtering: src-tauri/src/ai.rs
  • Media conversion and chunking: src-tauri/src/media.rs
  • Managed local Whisper runtime: src-tauri/src/bin/talkis-stt.rs
  • Call capture and call transcript assembly: src-tauri/src/call_capture.rs, src/lib/callCapture.ts

Stable principles:

  • Ordinary voice dictation should avoid ffmpeg in the hot path. Prefer native microphone capture that returns audio/wav, 16 kHz, mono, PCM16.
  • Keep WebView MediaRecorder as a fallback, especially when a selected microphone can only be addressed reliably by WebView deviceId.
  • Local STT input must be WAV 16 kHz mono PCM16; skip ffmpeg when audio already matches that format.
  • Keep ffmpeg for arbitrary files, video, unsupported formats, diarization preparation, and file chunking.
  • Long local Whisper jobs can hallucinate repeated caption-like text on silence. Preserve the no-context local runtime settings and the repetitive-text filters unless a replacement is tested against long silent recordings.
  • Call system-audio capture is implemented on all three desktop platforms: macOS via Core Audio (process/system tap), Windows via WASAPI loopback on the default output device (cpal), and Linux via a PipeWire monitor stream. Linux requires a running PipeWire daemon with an active output device; on PipeWire-less (pure PulseAudio) systems capture fails fast with a user-facing error by design.
  • Every audio path needs structured logs with enough evidence to debug runtime behavior: recorder stats, ffmpeg timing, STT endpoint, chunk index/size, and call-capture levels.

Release Workflow

  • Follow rules/release.rule.md for every release
  • Always update README.md before publishing a release
  • Create a per-release review file from docs/release/review-template.md
  • Push release work to release/vX.Y.Z first, then to main, then push tag vX.Y.Z
  • Treat .github/workflows/release.yml as the release automation source of truth

Key Conventions

  1. Language: UI text in Russian, code/comments in English
  2. Hotkeys: Format is Modifier+Key (e.g., Ctrl+Alt+Space), always validate with validateHotkey()
  3. Settings: Load once at startup via getSettings(), save immediately on change
  4. Window sizes: Widget is 50x18 in its compact state; keep window sizing in sync with src/windows/widget/widgetConstants.ts
  5. Logs location: ~/.talkis/talkis.log
  6. Package manager: Use bun everywhere (not npm/yarn)
  7. Dev-only features: Gate behind import.meta.env.DEV (e.g., Prompt Preview)

Issue Tracking with bd (beads)

IMPORTANT: This project uses bd (beads) for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods.

Why bd?

  • Dependency-aware: Track blockers and relationships between issues
  • Git-friendly: Dolt-powered version control with native sync
  • Agent-optimized: JSON output, ready work detection, discovered-from links
  • Prevents duplicate tracking systems and confusion

Quick Start

Check for ready work:

bd ready --json

Create new issues:

bd create "Issue title" --description="Detailed context" -t bug|feature|task -p 0-4 --json
bd create "Issue title" --description="What this issue is about" -p 1 --deps discovered-from:bd-123 --json

Claim and update:

bd update <id> --claim --json
bd update bd-42 --priority 1 --json

Complete work:

bd close bd-42 --reason "Completed" --json

Issue Types

  • bug - Something broken
  • feature - New functionality
  • task - Work item (tests, docs, refactoring)
  • epic - Large feature with subtasks
  • chore - Maintenance (dependencies, tooling)

Priorities

  • 0 - Critical (security, data loss, broken builds)
  • 1 - High (major features, important bugs)
  • 2 - Medium (default, nice-to-have)
  • 3 - Low (polish, optimization)
  • 4 - Backlog (future ideas)

Workflow for AI Agents

  1. Check ready work: bd ready shows unblocked issues
  2. Claim your task atomically: bd update <id> --claim
  3. Work on it: Implement, test, document
  4. Discover new work? Create linked issue:
    • bd create "Found bug" --description="Details about what was found" -p 1 --deps discovered-from:<parent-id>
  5. Complete: bd close <id> --reason "Done"

Quality

  • Use --acceptance and --design fields when creating issues
  • Use --validate to check description completeness

Lifecycle

  • bd defer <id> / bd supersede <id> for issue management
  • bd stale / bd orphans / bd lint for hygiene
  • bd human <id> to flag for human decisions
  • bd formula list / bd mol pour <name> for structured workflows

Auto-Sync

bd automatically syncs via Dolt:

  • Each write auto-commits to Dolt history
  • Use bd dolt push/bd dolt pull for remote sync
  • No manual export/import needed!

Important Rules

  • Use bd for ALL task tracking
  • Always use --json flag for programmatic use
  • Link discovered work with discovered-from dependencies
  • Check bd ready before asking "what should I work on?"
  • Do NOT create markdown TODO lists
  • Do NOT use external issue trackers
  • Do NOT duplicate tracking systems

For more details, see README.md and docs/QUICKSTART.md.

Session Completion

When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.

MANDATORY WORKFLOW:

  1. File issues for remaining work - Create issues for anything that needs follow-up
  2. Run quality gates (if code changed) - Tests, linters, builds
  3. Update issue status - Close finished work, update in-progress items
  4. PUSH TO REMOTE - This is MANDATORY:
    git pull --rebase
    bd dolt push
    git push
    git status  # MUST show "up to date with origin"
    
  5. Clean up - Clear stashes, prune remote branches
  6. Verify - All changes committed AND pushed
  7. Hand off - Provide context for next session

CRITICAL RULES:

  • Work is NOT complete until git push succeeds
  • NEVER stop before pushing - that leaves work stranded locally
  • NEVER say "ready to push when you are" - YOU must push
  • If push fails, resolve and retry until it succeeds