Lightweight planner that chains multiple tool calls from a single natural language request — e.g. "capture this article, add a todoist task to read it, and note it in today's daily". Planning layer on top of the existing bounded tool loop.
Lighter version of #19. On session open, silently check todoist/recent sessions and seed the system prompt with "context of the day" — no explicit /brief needed. Subsumes the parked proactive nudges item.
Unified cross-vault search across tars vault and personal vault with source attribution. Currently separate indexes (tars.db and notes.db); merge results with provenance tags.
Local speech-to-text (Whisper or MLX whisper) piped through existing conversation flow. Apple Silicon is fast enough for real-time local transcription. Makes tars usable hands-free.
Parked
Superseded by #43. Pattern detection over session history for suggestions.
Make max_search_context_tokens adaptive based on routing: allow a larger search budget for local-only conversations (cheap), tighten for Claude-routed requests unless the user explicitly asked for deep recall. Keeps default Claude cost low.
Cap each system prompt section (procedural, memory, search_context, daily) independently so one section can't starve the others. Makes compaction triggers more predictable and keeps the total prompt stable over time.
Done
Loads Memory.md and Procedural.md, sends them to the model with "find duplicates, stale entries, test data, and contradictions — propose removals", lets the user approve. Same interactive pattern as /review but for memory quality instead of corrections.
Session logs exist but there's no way to browse them. /sessions shows recent session summaries (date + topic line), /session <query> searches them. Makes episodic memory visible — "what did we talk about on Tuesday?"
memory_remember currently appends blindly. Before writing, search existing entries for near-duplicates (exact substring match is enough). Procedural.md rule says "use memory_recall before adding" but the tool doesn't enforce it.
Runs todoist_today + weather_now + weather_forecast in parallel, formats into a single digest. One command instead of three.
Instead of binary right/wrong routing, the model emits a confidence signal. When uncertain whether a request is a tool call or chat, tars asks before acting. Primarily a system prompt / procedural rule change.
The GET /search endpoint exists but the web UI doesn't use it. Add a /search command in web chat that renders results inline, matching CLI parity.
Appends to today's Obsidian daily note (or creates it). "Note: interesting idea about X" → appends to 2026-02-20.md in the vault. Bridges tars and existing note-taking.
IMAP polling + SMTP reply via dedicated Gmail account with app password. Polls inbox every 60s, processes emails from whitelisted senders through conversation.py, replies in-thread. Supports slash commands for reliable tool execution. Python stdlib only.
Keyword-based pre-routing: local model for chat, Claude for tool calls. Falls back gracefully on API errors (rate limits, billing, outages). Logs routing decisions to stderr for observability.
/stats command showing DB size, file count, chunk count, embedding model/dimensions, and session count. Available in CLI, web UI, and API (GET /stats).
Point the indexer at the full personal Obsidian vault (not just the tars vault). Tars can answer questions about your own notes, surface relevant context from past thinking. Implemented as tars notes-index (builds notes.db in TARS_NOTES_DIR) and /find <query> REPL command.
Fetch and extract text content from web pages. Model calls web_read when a user shares a URL. HTML stripped to text via stdlib parser, truncated to 12k chars. Available as /read in email.
Fetch a web page, optionally summarize with the model (stripping boilerplate), save to TARS_NOTES_DIR/17 tars captures/ with YAML frontmatter. Available in CLI and email.
When /capture is called mid-conversation, recent conversation context is passed to the summarization prompt so the summary emphasizes aspects relevant to what's being discussed. Email captures remain contextless (standalone by nature).
_clean_args() strips empty strings and None values from tool args at the run_tool() boundary. Fixes the class of issues where ollama models fill in every optional parameter with empty strings.
Add a /find <query> REPL command and tars notes-index CLI subcommand that searches the personal Obsidian vault (TARS_NOTES_DIR) via a separate notes.db index. Same chunk → embed → sqlite-vec pipeline as tars memory, with hidden directories (.obsidian, .trash) and inline base64 images excluded.
tars schedule CLI manages OS-level timers (launchd on macOS, systemd on Linux) for any tars subcommand. Supports calendar triggers (--hour/--minute) and file watchers (--watch). Includes tars schedule test to dry-run with baked environment. Replaces bin/tars-schedule-{mac,linux} scripts.
/brief results emailed on a schedule via tars email-brief subcommand. Wired to OS scheduling via tars schedule add.
Telegram bot polling channel via python-telegram-bot. Persistent reply keyboard for one-tap commands, slash command dispatch, conversation support, daily brief sender (tars telegram-brief). User filtering via TARS_TELEGRAM_ALLOW user IDs.
Heading context breadcrumbs on chunks (H1 > H2 > H3), reduced chunk size (800 → 400 tokens), list cohesion (score 5 → 1), and context-aware embeddings. Heading context is prepended to embed input only — stored content and FTS unchanged. Tagged find-v1 before changes. Requires reindex.
RouteResult with tool hints and procedural memory injected into system prompt. When Memory.md says "I use Todoist for todos" and the user says "remind me to buy eggs", route confidently to todoist.
_apply_review() in cli.py calls build_index() after writing rules to Procedural.md. Incremental indexing detects the changed content_hash and re-indexes only the modified file.
Processing failures now retry instead of immediately sending error replies. Slash dispatch wrapped in try/except. Empty body emails get a reply instead of silent Seen flag. Max-retry path logs clearly. BODY.PEEK ensures messages stay unseen until successfully processed and replied to.
Enhance /capture with metadata extraction: author, publish date, tags, reading time. Store in YAML frontmatter. Could also extract and save key quotes or generate a TL;DR alongside the full summary. Makes captures more useful as Obsidian notes.
Tab completion for slash commands, inline help for tool arguments, friendlier error messages (_format_error() classifies network, auth, config errors instead of raw "Tool error:" prefix).
Conversation history sidebar (active conversations + session history, click to view/load). Model indicator on streamed responses (meta SSE event after done). New API endpoints: GET /conversations/{id}/messages, GET /sessions/{filename}.
channel field on Conversation dataclass. Session filenames include channel suffix (2026-03-01T10-00-00-cli.md). SessionInfo.channel parsed from filename. Daily memory entries tagged with channel ([cli] session saved — ...). /sessions output shows channel tags.
/export formats conversation messages as markdown. Available in CLI, web, Telegram, and email via shared commands.dispatch(). Web UI has dedicated GET /conversations/{id}/export endpoint.
Wired up all slash commands missing from the web UI: /find, /read, /mcp, /schedule, /export. Added API endpoints for /find (notes search), /mcp (server list), /schedule (OS + in-process), and /export. Updated web /help text to match CLI.
Native Strava integration via stravalib. OAuth token lifecycle (auto-refresh), strava_activities tool (period filters, type filters, by-ID with laps/splits), strava_user tool (profile, stats, zones, gear). CLI strava-auth subcommand for one-time OAuth setup. Tokens stored in memory dir with 0o600.
strava_summary (period totals by type), strava_compare (period-over-period deltas), strava_analysis (trend comparison with auto-derived prior period), strava_routes (route listing, detail, starred segments), strava_zones (HR stream-based 3-zone distribution, polarised/pyramidal/threshold-heavy/unstructured classification). Format renderers with sparklines and zone bar charts. Router keyword patterns for all Strava tools. Domain prompt template assessed — model handles interpretation from tool descriptions; zone classification label + flag passed in data for steering.
/continue command picks up context from the most recent session regardless of which channel created it. Shared session lookup hydrates prior messages into a new Conversation.
Extracts [[wikilinks]] during indexing into a file_links table, resolves targets within the same collection, applies post-RRF graph proximity boost to search results linked to high-scoring hits. Filters embedded assets (images, PDFs, etc) from link extraction.
/memory-review command combines /tidy + /review into a single digest. Not CLI-only — dispatchable by TaskRunner from scheduled channel. CLI subcommands: tars review (stdout), tars email-review, tars telegram-review. Schedulable via OS-level timers or in-process schedules.json.
API auth warning: lifespan() warns when TARS_API_TOKEN is unset; cli.py logs ERROR when binding to non-localhost without a token. Capture filename collision: counter suffix avoids silent overwrites (Article (date) 2.md). _clean_args required field validation: run_tool() checks required fields from schema before dispatch, returns clear error for missing fields.