Skip to content

feat: tools enhancements + AI foundation#107

Merged
genesiscz merged 12 commits intomasterfrom
feat/enhancements
Mar 18, 2026
Merged

feat: tools enhancements + AI foundation#107
genesiscz merged 12 commits intomasterfrom
feat/enhancements

Conversation

@genesiscz
Copy link
Owner

@genesiscz genesiscz commented Mar 17, 2026

Summary

  • Fix zsh hook bugs (notify escaping, dotdotdot FUNCNEST recursion, Tab completion with dot preservation)
  • Add performance benchmarks to CLAUDE.md
  • Implementation plans for 5 new tools: notify, say, timer, benchmark, AI foundation
  • Plans for AI CLI tools: transcribe, youtube transcribe, voice-memos, tools ai

Planned tools

  • tools notify — reliable macOS notifications via terminal-notifier
  • tools say — TTS with volume control, per-app mute, dynamic voice map
  • tools timer — focus timer with notify/say integration
  • tools benchmark — hyperfine wrapper with named suites
  • tools transcribe — universal audio transcription (local HF + cloud)
  • tools youtube transcribe — YouTube captions + audio fallback
  • tools macos voice-memos — list/play/export/transcribe voice memos
  • tools ai — unified AI CLI (translate, summarize, image, classify, models)
  • src/utils/ai/ — AI utility layer with 3 providers (cloud, local-hf, darwinkit)

Test plan

  • Zsh fixes: source ~/.zshrc twice without FUNCNEST error
  • Dotdotdot: cd .../Tres<TAB> completes with dots preserved
  • Each new tool: manual testing per plan file

Summary by CodeRabbit

  • New Features

    • Unified AI toolkit (transcribe, YouTube transcribe, translate, summarize, image gen, models, config, interactive CLI).
    • macOS Voice Memos CLI: list, play, export, transcribe (embedded or AI), search, interactive mode.
    • New CLIs: benchmark, timer (foreground/background, pomodoro), notify, say; zsh "speak" shortcut.
  • Improvements

    • Richer macOS notifications and TTS (per-app/global mute, voice/volume, wait/background, improved fallbacks).
    • Better transcription UX: multiple output formats (text/json/SRT/VTT), language detection, chunking for large audio, robust audio conversion and clearer errors.
    • AI provider management, model cache tools, and config flows.
  • Tests

    • Extensive unit and end-to-end coverage for AI, audio, CLI tools, timers, notifications, and voice memos.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a provider-based AI layer (types, config, ModelManager, providers, tasks), extensive macOS utilities (TTS, notifications, Voice Memos), audio conversion, multiple new CLIs (ai, transcribe, youtube, voice‑memos, notify, say, timer, benchmark), zsh feature updates, tests, and four new package dependencies.

Changes

Cohort / File(s) Summary
AI core & types
src/utils/ai/types.ts, src/utils/ai/AIConfig.ts, src/utils/ai/ModelManager.ts, src/utils/ai/index.ts
Add typed AI contracts, persistent AIConfig, ModelManager for Hugging Face cache, and AI façade with lifecycle helpers.
Providers & registry
src/utils/ai/providers/...
Implement three providers (cloud, local‑hf, darwinkit), provider registry with lazy instantiation, availability checks, fallback selection, and dispose semantics.
AI task wrappers
src/utils/ai/tasks/*
Add Transcriber, Translator, Summarizer classes with factory create(), provider resolution, chunking/progress, and disposal.
Language detection
src/utils/ai/LanguageDetector.ts, src/utils/ai/LanguageDetector.test.ts
Multi-driver language detector (Whisper, MMS‑LID, DarwinKit), drivers, factory and unit tests.
Transcription formatting & CLI
src/utils/ai/transcription-format.ts, src/transcribe/index.ts
Subtitle/timecode formatting (text/json/srt/vtt) and full transcribe CLI with interactive and non-interactive modes.
YouTube transcription
src/youtube/index.ts, src/youtube/commands/transcribe.ts, src/youtube/commands/transcribe.test.ts
Captions-first fast path with audio fallback (yt-dlp), download/transcode, formatting, interactive prompts, and tests.
Local HF provider & ModelManager
src/utils/ai/providers/AILocalProvider.ts, src/utils/ai/ModelManager.ts
Local HF pipelines with dynamic imports, pipeline caching, model download/cleanup, progress, and corrupted-cache handling.
Cloud provider
src/utils/ai/providers/AICloudProvider.ts
Cloud provider wrapper using TranscriptionManager and dynamic cloud SDK imports; temp-file guarded transcription and model selection.
DarwinKit provider & macOS AI
src/utils/ai/providers/AIDarwinKitProvider.ts, src/utils/macos/*
DarwinKit-backed provider for classify/embed/sentiment and expanded macOS exports for NLP/TTS/Voice Memos.
macOS TTS & notifications
src/utils/macos/tts.ts, src/utils/macos/notifications.ts, src/notify/index.ts, src/say/index.ts
Storage-backed TTS (voice map, per-app/global mute, volume, wait), async notifications preferring terminal‑notifier with osascript fallback, and CLI wrappers with interactive config.
Voice Memos support & CLI
src/utils/macos/voice-memos.ts, src/macos/commands/voice-memos/index.ts, src/voice-memos/index.ts
Read-only Voice Memos DB discovery, tsrp transcript extraction, public Voice Memos API, CLI (list/play/export/transcribe/search/interactive), and macOS command registration.
Audio conversion & tests
src/utils/audio/converter.ts, src/utils/audio/converter.test.ts
Normalize audio to 16kHz mono WAV/Float32 with afconvert/ffmpeg fallbacks; WAV parser and unit tests.
Timer & Benchmark CLIs
src/timer/index.ts, src/benchmark/index.ts
Timer (foreground/background persistence, actions, list/cancel) and benchmark (hyperfine wrapper, suites, storage, run/compare).
Zsh features & wiring
src/zsh/features/dotdotdot.ts, src/zsh/features/notify.ts, src/zsh/features/speak.ts, src/zsh/features/index.ts
dotdotdot completion-aware expansion, notify frontmost-aware gating and Bash integration, new speak feature and registry update.
Terminal & macOS helpers
src/utils/terminal.ts, src/utils/macos/system-settings.ts, src/utils/macos/MacOS.ts
Add detectTerminalApp helper, system-settings openers for privacy panes, and MacOS namespace export.
Tests & e2e
src/e2e/*, src/timer/timer.test.ts, src/transcribe/transcribe.test.ts, src/utils/...tests
Extensive unit and end-to-end tests for AI tooling, transcribe, timer, notify, say, youtube, benchmark, voice‑memos, language detection, audio converter, and helpers.
Package & ignore
package.json, .gitignore
Add dependencies: @huggingface/inference, @huggingface/transformers, onnxruntime-node, youtube-transcript; update .gitignore for audio/video artifacts.
GitHub CLI option changes
src/github/commands/issue.ts, src/github/commands/pr.ts, src/github/types.ts
Replace boolean --save-locally with flexible --save [path] (string
Misc exports & small utilities
src/utils/macos/index.ts, src/utils/ai/providers/index.ts, src/utils/macos/notifications.ts
Expand macOS public exports (Voice Memos, TTS), provider barrel exports, provider registry functions, and change sendNotification to async.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI (transcribe / youtube / ai)
  participant FS as Filesystem/Temp
  participant Audio as AudioProcessor
  participant Registry as ProviderRegistry
  participant Provider as Provider (local/cloud/darwinkit)
  participant ModelMgr as ModelManager
  participant Task as AI.Task (Transcriber)

  CLI->>FS: accept file or URL
  CLI->>Audio: preprocess/convert audio
  Audio->>FS: read/write temp audio
  CLI->>Registry: request provider for "transcribe"
  Registry->>Provider: instantiate or return cached provider
  alt Provider requires model
    Provider->>ModelMgr: ensure model downloaded
    ModelMgr->>FS: read/write model cache
  end
  CLI->>Task: create Transcriber(provider/model opts)
  Task->>Provider: transcribe(audio, options) (with progress callbacks)
  Provider-->>Task: TranscriptionResult (text, segments, lang)
  Task-->>CLI: format output (text/json/srt/vtt)
  CLI->>FS: write output or print/clipboard
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/enhancements

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly expands the GenesisTools CLI by addressing existing Zsh hook issues and establishing a robust foundation for future AI-powered functionalities. It introduces detailed design and implementation plans for a suite of new tools aimed at enhancing user productivity and integrating advanced AI capabilities directly into the command line, ranging from improved system notifications and text-to-speech to comprehensive audio transcription and AI model management.

Highlights

  • Zsh Hook Enhancements: Fixed several bugs in Zsh hooks, including notify escaping issues and dotdotdot recursion, while also improving Tab completion for dotdotdot.
  • AI Foundation Development: Laid the architectural groundwork for a comprehensive AI toolkit (src/utils/ai/), defining provider interfaces, configuration management, model handling, and core task classes for transcription, translation, and summarization.
  • New Tool Planning: Detailed design and implementation plans were added for several new CLI tools: tools notify (reliable macOS notifications), tools say (TTS with volume control), tools timer (focus timer), tools benchmark (hyperfine wrapper), tools transcribe (universal audio transcription), tools youtube transcribe (YouTube video transcription), tools macos voice-memos (Voice Memos manager), and a unified tools ai CLI.
  • Performance Benchmarks: Added performance benchmarks to CLAUDE.md to measure the overhead of Bun tool invocations and different macOS notification methods.
Changelog
  • .claude/plans/2026-03-16-AITools.md
    • Documented existing AI infrastructure and outlined the design for new AI-powered CLI tools.
    • Listed new dependencies required for upcoming AI features.
  • .claude/plans/2026-03-16-ToolsEnhancements.md
    • Included performance benchmarks for various notification methods.
    • Detailed the design for several new CLI tools: notify, say, timer, benchmark, and claude statusline.
    • Summarized completed Zsh hook fixes.
  • .claude/plans/2026-03-17-AITools.md
    • Defined the architecture for the src/utils/ai/ utility layer, including file structure, provider interfaces, and three provider tiers (Cloud, Local HF, DarwinKit).
    • Described the AIConfig class and ModelManager for AI model handling.
    • Provided benchmarks for local HuggingFace inference.
    • Outlined CLI tools (tools transcribe, tools youtube transcribe, tools macos voice-memos, tools ai) that will leverage this AI foundation.
  • .claude/plans/2026-03-17-Plan0-CommitZshFixes.md
    • Provided a step-by-step implementation plan for committing Zsh fixes and updating CLAUDE.md.
  • .claude/plans/2026-03-17-Plan1-NotifyAndSay.md
    • Detailed tasks for upgrading src/utils/macos/notifications.ts to use terminal-notifier and creating src/notify/index.ts CLI.
    • Outlined tasks for upgrading src/utils/macos/tts.ts with dynamic voice maps, volume control, and mute support, and creating src/say/index.ts CLI.
  • .claude/plans/2026-03-17-Plan2-TimerAndBenchmark.md
    • Detailed tasks for creating src/timer/index.ts with duration parsing, core timer logic, interactive flow, subcommands, and Pomodoro support.
    • Outlined tasks for creating src/benchmark/index.ts with hyperfine integration, suite storage, and interactive management.
  • .claude/plans/2026-03-17-Plan3-AIFoundation.md
    • Detailed tasks for installing new dependencies (@huggingface/transformers, onnxruntime-node, youtube-transcript).
    • Outlined the creation of AI types, provider interfaces, AIConfig class, ModelManager, various AI providers, and core task classes (Transcriber, Translator, Summarizer).
  • .claude/plans/2026-03-17-Plan4-AICLITools.md
    • Detailed tasks for creating tools transcribe CLI with interactive mode and output formatting.
    • Outlined tasks for tools youtube transcribe with caption fast path and audio fallback.
    • Described the unified tools ai CLI with subcommands for translation, summarization, image generation, model management, and configuration.
  • .claude/plans/2026-03-17-Plan5-VoiceMemos.md
    • Detailed tasks for creating src/utils/macos/voice-memos.ts utility for database access and tsrp transcript extraction.
    • Outlined the creation of src/voice-memos/index.ts CLI with list, play, export, transcribe, search, and interactive commands.
    • Included tasks for graceful handling of permissions and missing Voice Memos database.
  • .claude/plans/2026-03-17-TeamRules.md
    • Defined Git rules, agent instructions, and a mandatory reference for reusable utilities.
    • Highlighted pitfalls to avoid during implementation.
  • CLAUDE.md
    • Added a new section titled 'Performance Benchmarks' detailing execution times for Bun tool invocations and macOS notifications.
  • src/zsh/features/dotdotdot.ts
    • Modified the dotdotdot feature to fix FUNCNEST recursion and improve Tab completion with dot preservation.
    • Added logic to prevent circular references when re-sourcing Zsh configuration.
  • src/zsh/features/notify.ts
    • Fixed osascript escaping issues in the notify feature to prevent AppleScript injection.
    • Added comprehensive comments to explain the Zsh and Bash hook mechanisms.
    • Improved command truncation logic for notifications.
Activity
  • No human activity (comments, reviews) was detected on this pull request.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant amount of new functionality, primarily through detailed design and implementation plans for new tools and an AI foundation. It also includes fixes for zsh hooks. The plans are well-structured, but there are a few areas that could be improved for robustness and clarity. Specifically, I've noted a contradiction in naming conventions, potential race conditions with temporary files, overwriting of benchmark results, and use of magic numbers and hardcoded values. The zsh hook fixes appear solid and address the described issues.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/plans/2026-03-17-AITools.md:
- Around line 94-100: Replace storing the plaintext "hfToken" in the config with
an env/keychain-first approach: update the config schema to hold a token
reference (e.g., "hfTokenRef" or null) instead of the raw token and modify the
code that reads the config to resolve the token by checking (1) environment
variable HF_TOKEN, (2) OS keychain (or secret manager) lookup keyed by the token
reference, and only fall back to using a token from config if explicitly
present. Ensure any code paths that write the config (creation/CLI or saveConfig
functions) write only the token reference/placeholder and never persist the raw
"hfToken" by default, and add a clear comment in the config handling logic to
document the env/keychain-first resolution.

In @.claude/plans/2026-03-17-Plan0-CommitZshFixes.md:
- Line 39: In the git add command in
.claude/plans/2026-03-17-Plan0-CommitZshFixes.md update the incorrect filename
reference: replace 2026-03-17-AITools.md with the correct 2026-03-16-AITools.md
so the command becomes git add .claude/plans/2026-03-16-AITools.md (keeping the
other filenames unchanged); locate the git add line in that file and correct the
filename to match the actual AITools document.

In @.claude/plans/2026-03-17-Plan1-NotifyAndSay.md:
- Around line 209-222: The plan currently adds an alias named sayy which
conflicts with TeamRules that require speak; update the zsh hook/feature
(src/zsh/features/port.ts or the tools zsh install flow) to create the alias
speak instead of sayy, remove any references to sayy in the plan and install
steps, and update related docs/tests that reference sayy so they use speak; if
backward-compatibility is needed, add a short migration note in the install flow
but keep speak as the canonical alias.

In @.claude/plans/2026-03-17-Plan2-TimerAndBenchmark.md:
- Around line 20-23: The plan's Step 1 should not reimplement duration parsing;
instead use the existing parseDuration() utility from src/utils/format.ts to
avoid drift—remove the new parser logic and update the plan to call or reference
parseDuration() (which returns milliseconds) for inputs like "25m", "1h30m",
"45" (minutes default), ensuring the timer/benchmark code consumes
parseDuration() output directly.

In @.claude/plans/2026-03-17-Plan3-AIFoundation.md:
- Line 43: The AITask union type is missing "language-detect" while
supports(task: AITask) checks for it; update the type or the method to match:
either add "language-detect" to the AITask union definition (export type AITask
= "... | \"language-detect\";") or remove/replace "language-detect" from the
array inside the supports(task: AITask) method so the implementation and the
AITask type are consistent (refer to the AITask type declaration and the
supports(task: AITask) method).

In @.claude/plans/2026-03-17-Plan4-AICLITools.md:
- Line 65: The plan references two different YouTube CLI directories (one as
"youtube" and one as "youtube-transcribe"), causing staging/implementation
drift; pick one canonical directory name and update all plan entries to match it
(rename the module entry reference to the same directory that holds the CLI
entry module and the transcribe command), ensuring the module entry (index.ts)
and the transcribe command (commands/transcribe.ts) are referenced consistently
and any staging or commit commands in the plan use the chosen directory name.

In @.claude/plans/2026-03-17-Plan5-VoiceMemos.md:
- Around line 121-123: The export plan currently uses the raw memo title when
constructing the output filename for the `tools macos voice-memos export <id>
[dest]` command (pattern `YYYY-MM-DD-title.m4a`), which can produce invalid or
unsafe paths; update the plan and any implementation notes to explicitly
sanitize the title by removing or replacing path separators (/, \), colons,
control characters, and null bytes, collapse whitespace, trim/limit filename
length, and forbid path traversal sequences (e.g., "../"); if the sanitized
title is empty, fall back to the memo ID; document the sanitization rules in the
plan so implementers use a canonical sanitize function.

In @.claude/plans/2026-03-17-TeamRules.md:
- Around line 4-14: The rule "Agents do NOT commit." is ambiguous when plan
files contain explicit git commit steps; update the team rules text (the "Agents
do NOT commit." line and the "Agent instructions" section) to explicitly state
that any git commit or commit-snippet appearing inside plan files are
leader-only instructions and must not be executed by agents, and add a short
line instructing agents to ignore commit commands in plans (e.g., "Commit
snippets in plans are for the leader/main session only; agents must not run git
commit"). Also ensure the clarification references plan files and the existing
CLAUDE.md rule set so agents know to follow CLAUDE.md typing and execution
constraints while treating commit steps as leader-only.

In `@src/zsh/features/dotdotdot.ts`:
- Line 71: The top-level declaration uses "local __tab_widget=..." which errors
when the file is sourced because local is function-scoped; change that line to
either move the assignment into the function that uses __tab_widget or make it a
global/top-level variable by replacing "local" with "typeset -g" (or removing
"local" entirely) so the assignment __tab_widget=${$(bindkey "^I")[(w)2]} does
not use function-scoped local at top-level.

In `@src/zsh/features/notify.ts`:
- Around line 9-31: Remove the obvious inline comments in the shell block that
merely restate syntax or behavior (e.g. comments explaining that preexec/precmd
run before/after commands, that $SECONDS counts seconds, that (( )) is
arithmetic, and explanations of ${`#cmd`} or ${cmd:0:57} semantics). Edit the
functions __genesis_notify_preexec and __genesis_notify_precmd to keep only
meaningful, non-redundant comments (such as rationale for threshold logic or why
exit_code is captured first) and delete the rest so the code is concise while
preserving comments that explain intent or non-obvious behavior.
- Around line 53-56: The frontmost-app comparison is brittle because
TERM_PROGRAM values (term_app) may differ from AppleScript's active_app names
(e.g., iTerm vs iTerm2); modify the notify logic to normalize and map values
before comparing: normalize both active_app and term_app to lowercase and strip
any ".app" suffix, then apply a small mapping table for known terminals/editors
(e.g., TERM_PROGRAM "iTerm.app" or "iTerm2" -> "iterm2", "vscode" -> "code",
"Apple_Terminal" -> "terminal") or, better, compare bundle identifiers if
available, and finally use the normalized/mapped values (active_app vs term_app)
for the if-check so notifications are suppressed when the terminal is actually
frontmost.
- Around line 80-97: The DEBUG trap is firing for the PROMPT_COMMAND itself and
clobbering __genesis_notify_cmd/__genesis_notify_start; modify
__genesis_notify_preexec to early-return when BASH_COMMAND is the precmd handler
or related internal handlers. Add a guard at the top of __genesis_notify_preexec
that checks if "$BASH_COMMAND" equals "__genesis_notify_precmd" or matches
"__genesis_notify_precmd"* (and optionally "__genesis_notify_preexec"/other
internal helper names) and return immediately so the trap ignores PROMPT_COMMAND
invocation and doesn't overwrite the tracked command/timer used by
__genesis_notify_precmd.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 24028923-8dde-4cf5-98c5-67917fe1b860

📥 Commits

Reviewing files that changed from the base of the PR and between 4aa18ec and 5f87acb.

📒 Files selected for processing (13)
  • .claude/plans/2026-03-16-AITools.md
  • .claude/plans/2026-03-16-ToolsEnhancements.md
  • .claude/plans/2026-03-17-AITools.md
  • .claude/plans/2026-03-17-Plan0-CommitZshFixes.md
  • .claude/plans/2026-03-17-Plan1-NotifyAndSay.md
  • .claude/plans/2026-03-17-Plan2-TimerAndBenchmark.md
  • .claude/plans/2026-03-17-Plan3-AIFoundation.md
  • .claude/plans/2026-03-17-Plan4-AICLITools.md
  • .claude/plans/2026-03-17-Plan5-VoiceMemos.md
  • .claude/plans/2026-03-17-TeamRules.md
  • CLAUDE.md
  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Do not add file-path comments as the first line of files
Do not add obvious comments that restate what the code already says
Never use one-line if statements; always use block form with braces
Include an empty line before if statements, unless the preceding line is a variable declaration used by that if
Include an empty line after closing } unless followed by else, catch, finally, or another }
Use object parameters for functions with 3+ parameters or optional parameters
Use positional parameters only for 1-2 required and obvious parameters such as estimateTokens(text) or resolve(base, path)
Prefer error: err over error: err instanceof Error ? err.message : String(err) when the error field accepts unknown

Files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Do not use as any for type casting; use proper type narrowing, type guards, or explicit interfaces instead
Use discriminant checks for union types (e.g., entity.className === "User")

Files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
🧠 Learnings (24)
📓 Common learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.
📚 Learning: 2026-02-17T01:37:29.373Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 29
File: .claude/plans/2026-02-16-ClaudeHistory-SummarizeMemorize-Implementation.md:11-15
Timestamp: 2026-02-17T01:37:29.373Z
Learning: Do not apply markdownlint to files under the .claude/ directory. This exemption covers design documents, plans, and archived content within .claude/ (e.g., .claude/plans/.../*.md). Treat these files as exempt from markdownlint rules to avoid false positives on non-standard or archival content.

Applied to files:

  • .claude/plans/2026-03-17-TeamRules.md
  • .claude/plans/2026-03-17-Plan2-TimerAndBenchmark.md
  • .claude/plans/2026-03-17-Plan0-CommitZshFixes.md
  • .claude/plans/2026-03-17-Plan1-NotifyAndSay.md
  • .claude/plans/2026-03-16-ToolsEnhancements.md
  • .claude/plans/2026-03-17-AITools.md
  • .claude/plans/2026-03-17-Plan5-VoiceMemos.md
  • .claude/plans/2026-03-16-AITools.md
  • .claude/plans/2026-03-17-Plan3-AIFoundation.md
  • .claude/plans/2026-03-17-Plan4-AICLITools.md
📚 Learning: 2026-02-15T17:29:24.888Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 27
File: .claude/old/commands/implement-v2.md:10-12
Timestamp: 2026-02-15T17:29:24.888Z
Learning: Files in the `.claude/old/` directory are archived for historical reference and should be preserved as-is. Do not suggest fixes or cleanup for files in this directory, including markdown linting issues or code formatting changes.

Applied to files:

  • .claude/plans/2026-03-17-TeamRules.md
  • .claude/plans/2026-03-17-Plan0-CommitZshFixes.md
📚 Learning: 2026-03-09T13:39:43.059Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.

Applied to files:

  • .claude/plans/2026-03-17-Plan1-NotifyAndSay.md
  • .claude/plans/2026-03-16-ToolsEnhancements.md
📚 Learning: 2026-03-15T23:02:14.970Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.970Z
Learning: Applies to src/**/index.{ts,tsx} : Use `clack/prompts` for interactive user experience in new tools; use `inquirer/prompts` only for legacy tools

Applied to files:

  • .claude/plans/2026-03-17-Plan1-NotifyAndSay.md
  • .claude/plans/2026-03-16-ToolsEnhancements.md
  • .claude/plans/2026-03-17-Plan4-AICLITools.md
📚 Learning: 2026-02-18T00:01:36.425Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 29
File: src/claude-skill-to-desktop/lib.ts:0-0
Timestamp: 2026-02-18T00:01:36.425Z
Learning: The `claude-skill-to-desktop` tool in `src/claude-skill-to-desktop/` is macOS-only by design. The `CLAUDE_DESKTOP_BASE` path using `Library/Application Support/Claude` is intentional and cross-platform support is not intended for this tool.

Applied to files:

  • .claude/plans/2026-03-16-ToolsEnhancements.md
📚 Learning: 2026-02-20T00:52:27.023Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 31
File: src/ask/utils/helpers.ts:3-3
Timestamp: 2026-02-20T00:52:27.023Z
Learning: In all TypeScript source files under src, prefer using picocolors for colored terminal output in new code. Picocolors is smaller and faster than chalk, so adopt it for CLI output coloring and avoid adding chalk in new code paths unless there is a compelling compatibility reason.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-15T16:03:42.969Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:9-12
Timestamp: 2026-03-15T16:03:42.969Z
Learning: In genesiscz/GenesisTools, for standalone bun scripts (e.g., `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`) that are invoked directly, the correct convention is `bun file.ts` (not `bun run file.ts`). `bun run` is reserved for package.json script entries. The shebang `#!/usr/bin/env bun` is used to mark such standalone scripts. Do NOT suggest replacing `bun file.ts` with `bun run file.ts` in usage/help text for standalone scripts.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T23:02:14.971Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.971Z
Learning: The project requires Bun as the runtime and uses Bun-specific APIs (e.g., `Bun.spawn`)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T23:02:14.970Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.970Z
Learning: Applies to src/**/index.{ts,tsx} : Use Bun's native file APIs (`Bun.write()`) for better performance in file operations

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T23:02:14.970Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.970Z
Learning: Applies to src/**/index.{ts,tsx} : Use `Bun.spawn()` for executing external commands and handle stdout/stderr streams properly

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T23:02:14.970Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.970Z
Learning: Applies to src/utils/**/*.{ts,tsx} : Place general-purpose utility functions in `src/utils/` instead of inside tool directories

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T23:02:14.971Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.971Z
Learning: TypeScript is configured in strict mode with ES modules and Bun executes TypeScript directly without a build step

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-02-25T23:00:07.620Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 56
File: plugins/genesis-tools/commands/github-pr.md:131-131
Timestamp: 2026-02-25T23:00:07.620Z
Learning: Adopt the style: use lowercase 'markdown' (not 'Markdown') in the GenesisTools documentation. Apply this consistently across all Markdown files in the repository (any .md file), including generated docs and READMEs.

Applied to files:

  • CLAUDE.md
🪛 LanguageTool
.claude/plans/2026-03-17-TeamRules.md

[uncategorized] ~7-~7: The official name of this popular video platform is spelled with a capital “T”.
Context: ...imer, benchmark, ai, transcribe, youtube, voice-memos` - All work goes to bran...

(YOUTUBE)


[uncategorized] ~16-~16: The official name of this popular video platform is spelled with a capital “T”.
Context: ...uses commands/ subfolder pattern (src/youtube/commands/transcribe.ts) ## MANDATORY: ...

(YOUTUBE)

.claude/plans/2026-03-17-Plan1-NotifyAndSay.md

[uncategorized] ~7-~7: The operating system from Apple is written “macOS”.
Context: ...y. **Architecture:** Upgrade existing src/utils/macos/notifications.ts` to use terminal-notif...

(MAC_OS)


[uncategorized] ~7-~7: The operating system from Apple is written “macOS”.
Context: ...eate src/notify/ CLI wrapper. Upgrade src/utils/macos/tts.ts with volume control + dynamic v...

(MAC_OS)


[uncategorized] ~17-~17: The operating system from Apple is written “macOS”.
Context: ...utils/macos/notifications.ts- Modify:src/utils/macos/index.ts` (update exports if needed) *...

(MAC_OS)

.claude/plans/2026-03-16-ToolsEnhancements.md

[uncategorized] ~32-~32: The operating system from Apple is written “macOS”.
Context: ...iable macOS notifications Utility: src/utils/macos/notifications.ts (upgrade existing) **...

(MAC_OS)


[style] ~47-~47: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ...r -appIcon - Default sound: Ping ### CLI interface ``` tools notify "Build complete" tools...

(ACRONYM_TAUTOLOGY)


[uncategorized] ~56-~56: The operating system from Apple is written “macOS”.
Context: ...— TTS with volume control Utility: src/utils/macos/tts.ts (upgrade existing speak()) **...

(MAC_OS)


[style] ~77-~77: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ...creates sayy alias → tools say ### CLI interface ``` tools say ...

(ACRONYM_TAUTOLOGY)


[style] ~109-~109: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ...er 25m "work" --notify --say --bg` ### CLI interface ``` tools timer ...

(ACRONYM_TAUTOLOGY)


[style] ~138-~138: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ... - say — TTS pipeline end-to-end ### CLI interface ``` tools benchmark ...

(ACRONYM_TAUTOLOGY)

.claude/plans/2026-03-17-AITools.md

[uncategorized] ~105-105: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...LI tools directly. - Download HF models on demand - Cache in `
/.genesis-tools/ai/models/...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~185-~185: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...heck before re-transcribing. - Requires Full Disk Access Commands: ``` tools macos voice...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

.claude/plans/2026-03-17-Plan5-VoiceMemos.md

[uncategorized] ~1-~1: The operating system from Apple is written “macOS”.
Context: # tools macos voice-memos — Implementation Plan > **...

(MAC_OS)


[uncategorized] ~5-~5: The operating system from Apple is written “macOS”.
Context: ...ice Memos. Includes a utility module at src/utils/macos/voice-memos.ts for database access and...

(MAC_OS)


[uncategorized] ~15-~15: The operating system from Apple is written “macOS”.
Context: ...fallback path. --- ### Task 1: Create src/utils/macos/voice-memos.ts utility Files: - C...

(MAC_OS)


[uncategorized] ~19-~19: The operating system from Apple is written “macOS”.
Context: ...c/utils/macos/voice-memos.ts- Modify:src/utils/macos/index.ts` (add exports) **Step 1: Impl...

(MAC_OS)


[uncategorized] ~29-~29: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ir }` or throw with helpful error about Full Disk Access. **Step 2: Implement database r...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~170-~170: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...macos/voice-memos.ts` Step 1: Detect Full Disk Access When SQLite open fails with p...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

.claude/plans/2026-03-16-AITools.md

[style] ~55-~55: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ...ram|gladia|hf` → specific provider ### CLI interface ``` tools transcribe ...

(ACRONYM_TAUTOLOGY)


[uncategorized] ~69-~69: The official name of this popular video platform is spelled with a capital “T”.
Context: ...-transcribe/index.ts(or subcommand oftools youtube`) ### Design - Two-phase approach:...

(YOUTUBE)


[uncategorized] ~81-~81: The official name of this popular video platform is spelled with a capital “T”.
Context: ...ipeline (local/cloud/HF) - Interactive: tools youtube transcribe → paste URL → shows caption...

(YOUTUBE)


[style] ~85-~85: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ...io transcription (better accuracy) ### CLI interface ``` tools youtube transcribe ...

(ACRONYM_TAUTOLOGY)


[uncategorized] ~114-~114: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ore running transcription. - Requires Full Disk Access for terminal to read the DB #...

(EN_COMPOUND_ADJECTIVE_INTERNAL)


[uncategorized] ~125-~125: The operating system from Apple is written “macOS”.
Context: ...es + transcriptions - Interactive mode (tools macos voice-memos): browse, play, export, tr...

(MAC_OS)


[style] ~135-~135: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ...TE IS NULL ORDER BY ZDATE DESC ### CLI interface tools macos voice-memos ...

(ACRONYM_TAUTOLOGY)


[style] ~188-~188: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ...ed "search query" --index ./docs/` ### CLI interface ``` tools ai ...

(ACRONYM_TAUTOLOGY)

.claude/plans/2026-03-17-Plan3-AIFoundation.md

[uncategorized] ~265-~265: The operating system from Apple is written “macOS”.
Context: ...arwinKit provider** Routes to existing src/utils/macos/* functions: ```typescript export cla...

(MAC_OS)

.claude/plans/2026-03-17-Plan4-AICLITools.md

[uncategorized] ~5-~5: The official name of this popular video platform is spelled with a capital “T”.
Context: ...ls/ai/foundation:tools transcribe, tools youtube transcribe, and tools ai` (unified AI...

(YOUTUBE)

🪛 markdownlint-cli2 (0.21.0)
.claude/plans/2026-03-17-Plan2-TimerAndBenchmark.md

[warning] 15-15: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)


[warning] 77-77: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

.claude/plans/2026-03-17-Plan0-CommitZshFixes.md

[warning] 8-8: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 9-9: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 9-9: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 28-28: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 29-29: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

.claude/plans/2026-03-17-Plan1-NotifyAndSay.md

[warning] 13-13: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)


[warning] 30-30: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 93-93: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 122-122: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 138-138: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 155-155: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 217-217: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

.claude/plans/2026-03-16-ToolsEnhancements.md

[warning] 15-15: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)


[warning] 48-48: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 173-173: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 173-173: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 183-183: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 183-183: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

.claude/plans/2026-03-17-AITools.md

[warning] 15-15: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)


[warning] 48-48: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 173-173: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 173-173: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 183-183: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 183-183: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

.claude/plans/2026-03-16-AITools.md

[warning] 8-8: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 9-9: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 9-9: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 28-28: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 29-29: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 68-68: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 69-69: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 82-82: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 83-83: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 92-92: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 103-103: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 110-110: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 117-117: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 129-129: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 149-149: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 166-166: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 188-188: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 188-188: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 205-205: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 205-205: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

.claude/plans/2026-03-17-Plan3-AIFoundation.md

[warning] 15-15: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)


[warning] 137-137: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 137-137: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (2)
CLAUDE.md (1)

58-67: Benchmark guidance is clear and actionable.

The new table and the async hook note improve operational decision-making for notification tooling.

src/zsh/features/dotdotdot.ts (1)

64-71: 🛠️ Refactor suggestion | 🟠 Major

Prune obvious comments around widget binding.

The added comments in this block mostly narrate exactly what the next line does; keep only comments with non-obvious rationale.

As per coding guidelines "Do not add obvious comments that restate what the code already says".

⛔ Skipped due to learnings
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Do not add obvious comments that restate what the code already says
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:31.715Z
Learning: In genesiscz/GenesisTools (src/**/*.ts), do not add inline comments for well-known POSIX patterns or standard idioms (e.g., process.kill(pid, 0) probe) when the surrounding code is already self-documenting via descriptive function/variable names. The project enforces a no-obvious-comments rule: avoid comments that merely restate what the code clearly expresses.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 24

♻️ Duplicate comments (2)
src/zsh/features/notify.ts (2)

50-56: ⚠️ Potential issue | 🟠 Major

Normalize terminal app names before comparing them.

TERM_PROGRAM and the AppleScript frontmost-app name still use different naming schemes (Apple_Terminal vs Terminal, iTerm.app vs iTerm2, vscode vs Code), so this check can still fire notifications while the terminal is frontmost.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/zsh/features/notify.ts` around lines 50 - 56, The comparison between
active_app and term_app can be false positives because their naming differs; add
a small normalization step (e.g., a helper function or inline mapping) to
canonicalize both names before comparing (referencing the variables active_app
and term_app in this block). Normalize common variants such as "Apple_Terminal"
and "Terminal" to "Terminal", "iTerm.app" and "iTerm2" to "iTerm", and
"Code"/"Visual Studio Code" to the TERM_PROGRAM value for VS Code (or vice
versa), then compare the normalized_active and normalized_term values so the
check only notifies when the user actually switched away. Ensure the
normalization handles case and strips ".app" suffixes.

77-93: ⚠️ Potential issue | 🔴 Critical

The Bash DEBUG trap still clobbers the tracked command.

The trap fires for __genesis_notify_precmd itself, so just before the state is read, __genesis_notify_cmd/__genesis_notify_start can be overwritten with the precmd handler and a near-zero duration. That makes long-running Bash notifications unreliable.

#!/bin/bash
bash <<'BASH'
__genesis_notify_preexec() {
  __genesis_notify_cmd="$1"
  __genesis_notify_start=$SECONDS
}
__genesis_notify_precmd() {
  local _=$?
  printf 'cmd=%s elapsed=%s\n' "$__genesis_notify_cmd" "$((SECONDS-__genesis_notify_start))"
}
trap '__genesis_notify_preexec "$BASH_COMMAND"' DEBUG
sleep 2
__genesis_notify_precmd
BASH
# Expected bad signal: cmd becomes __genesis_notify_precmd (or similar) with elapsed near 0.
🔧 Guard the internal handlers
 __genesis_notify_preexec() {
+    case "$1" in
+        __genesis_notify_preexec*|__genesis_notify_precmd*)
+            return
+            ;;
+    esac
+
     __genesis_notify_cmd="$1"
     __genesis_notify_start=$SECONDS
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/zsh/features/notify.ts` around lines 77 - 93, The DEBUG trap is firing
for the internal handlers and clobbering
__genesis_notify_cmd/__genesis_notify_start; modify the preexec/precmd logic so
internal handler invocations are ignored: add a guard in
__genesis_notify_preexec that returns immediately when $BASH_COMMAND matches the
internal handler names (__genesis_notify_precmd or __genesis_notify_preexec) or
when a short-lived guard flag (e.g. __genesis_notify_internal=1) is set, and
wrap __genesis_notify_precmd with that guard flag (set before running and unset
after) so the DEBUG trap won’t record the precmd call itself, thus preserving
the real command and start time.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/benchmark/index.ts`:
- Around line 147-148: The SafeJSON.parse calls that deserialize
hyperfine/result files (e.g., the one that reads Bun.file(join(RESULTS_DIR,
files[0])).text() and the similar call around lines 187-188) must enforce strict
RFC‑8259 parsing; update each SafeJSON.parse invocation to pass the options
object { strict: true } so the parse becomes SafeJSON.parse(content, { strict:
true }) and returns a strongly validated SavedResult. Locate all SafeJSON.parse
usages in src/benchmark/index.ts (including the two noted spots) and add the {
strict: true } option to each call.
- Around line 70-75: The built-in "notify" benchmark suite in
src/benchmark/index.ts hardcodes /opt/homebrew/bin/terminal-notifier which
breaks on Intel Homebrew and other PATH installs; update the "notify" suite (the
object with name: "notify", builtIn: true) to invoke the notifier via the
dynamic resolver or wrapper provided by src/utils/macos/notifications.ts instead
of the absolute path — either use the wrapper/CLI entrypoint exported by
notifications.ts or call the plain "terminal-notifier" (no prefix) so the
runtime PATH/resolver in notifications.ts can locate the correct binary.

In `@src/notify/index.ts`:
- Around line 101-102: The CLI options .option("--ignore-dnd") and
.option("--say") are currently positive-only so a persisted true in
config.ignoreDnD or config.say cannot be overridden to false via CLI; update the
option declarations for both the ignore-dnd and say flags (also where similar
options at 130–131 are declared) to be negatable (e.g., add the corresponding
--no-* forms or use Commander's negatable syntax) and then read the final value
so that CLI-provided booleans take precedence over config (i.e., prefer the
parsed option when defined, otherwise fall back to config.ignoreDnD /
config.say).
- Around line 114-116: Replace the call to program.help() in the CLI handling
branch with program.outputHelp() followed by an explicit process.exit(0); update
the block in src/notify/index.ts where the check uses program.help() (the CLI
argument handling branch) so that it calls program.outputHelp() and then invokes
process.exit(0) instead of relying on the unreachable return; remove the
now-unnecessary return statement.

In `@src/say/index.ts`:
- Around line 213-223: The default-voice picker is built from getVoiceMap()
which intentionally deduplicates voices by language, so replace that call with
the full-voice list provider (e.g. a function that returns all installed voices
instead of one-per-language) and build the options from that full list; update
the references in this block (getVoiceMap, voiceMap, voices, options) to use the
full-voice array (keeping fields name, locale, sample) so every installed voice
appears as a selectable default.
- Around line 17-69: Replace the synchronous program.parse() call with an async
entry that awaits program.parseAsync(process.argv) inside a try/catch so any
rejections from the action (which awaits setMute, interactiveMode, speak) are
handled; implement an async main() that awaits program.parseAsync(process.argv),
on error logs the message via p.log.error (or the project logger) and exits with
process.exit(1), then call main().

In `@src/utils/ai/ModelManager.ts`:
- Around line 1-5: Reorder the top-of-file imports in ModelManager.ts to satisfy
Biome/CI: group and sort them (built-in node modules first, alphabetically: from
"node:fs" then "node:os" then "node:path"), then third-party/internal imports
alphabetically (put logger from "@app/logger" and formatBytes from
"@app/utils/format" after the node imports), ensuring the import specifiers
themselves are sorted (e.g., existsSync, readdirSync, statSync, unlinkSync,
rmdirSync together) so the import list is deterministic and Biome will pass.
- Around line 120-133: The manual recursive deletion in removeDirRecursive
should be replaced with Node's built-in rmSync to simplify and harden removal;
update the removeDirRecursive implementation to call rmSync(dirPath, {
recursive: true, force: true }) (or use fs.rmSync) instead of
readdirSync/unlinkSync/rmdirSync traversal, so it correctly handles symlinks and
permission edge cases while preserving the method name and signature.

In `@src/utils/ai/providers/AICloudProvider.ts`:
- Around line 77-91: The code uses two different default labels for the source
language: a local const from = options.from ?? "auto-detect" (used inside
prompt) and the returned from: options.from ?? "auto" which is inconsistent;
pick one canonical default (e.g., "auto-detect" or "auto") and update both the
const from initialization and the return object's from field to use that same
string so prompt construction (prompt variable) and the returned metadata are
identical; adjust any tests or callers expecting the previous value accordingly.

In `@src/utils/ai/providers/AIDarwinKitProvider.ts`:
- Around line 43-46: The loop in AIDarwinKitProvider calling textDistance
currently hardcodes "en" and "sentence", which reduces accuracy for non-English
text; update the caller to determine language dynamically (e.g., call
detectLanguage(text) or accept a language parameter on the classification
method) and pass that detected/received language into textDistance instead of
"en" (also consider making the comparison type configurable rather than always
"sentence" and pass it through from the method signature); locate the category
scoring loop in AIDarwinKitProvider, adjust the surrounding method signature to
accept or compute language and comparisonType, and replace the hardcoded "en"
and "sentence" arguments to textDistance with the dynamic values.
- Line 38: The import in AIDarwinKitProvider.ts destructures both embedText and
textDistance but embedText is never used in the classify method; remove
embedText from the dynamic import (leave only textDistance) in the const { ... }
= await import(...) expression inside the AIDarwinKitProvider (and specifically
the classify method) to eliminate the unused variable.

In `@src/utils/ai/providers/AILocalProvider.ts`:
- Around line 28-30: The isAvailable() method in AILocalProvider currently
always returns true; change it to perform a real readiness check (e.g., verify
the model cache directory and required model files exist or validate a cached
manifest) and return false if required downloads are missing or unreadable, or
alternatively attempt a lightweight download/verification and fail fast; update
AILocalProvider.isAvailable to check the presence/permissions of the model cache
directory or a model-specific flag (and optionally defer to a lazy-initialize
method like loadModel()/ensureModelDownloaded()) so callers get accurate
availability status.
- Around line 58-72: The translate method builds a default model name assuming a
direct Helsinki-NLP pair which can fail for unsupported language pairs; update
the translate function to validate or gracefully handle missing models by
wrapping the getPipeline("translation", model) call in a try/catch and, on
failure, attempt a fallback strategy (e.g. pivot through English using two
pipelines: source->en and en->target) or return a clear, typed error indicating
unsupported language pair; reference the translate method, the model variable,
and getPipeline to locate where to add the try/catch and fallback logic.
- Around line 36-56: The transcribe() method in AILocalProvider currently
reinterprets encoded file buffers via new Float32Array(audio.buffer) which
yields garbage; decode the incoming Buffer to raw float32 PCM (mono, 16kHz as
expected by the pipeline) before passing to the pipeline. Update transcribe() to
call the project's audio decoding utility (e.g., AudioProcessor.decode/convert
or equivalent) to produce a Float32Array of samples, then pass that array to
getPipeline("automatic-speech-recognition", model) instead of raw encoded bytes;
ensure language/options handling remains unchanged and reuse getPipeline(...)
and TranscribeOptions symbols.

In `@src/utils/ai/providers/index.ts`:
- Around line 65-68: The function getAvailableProviders currently returns
instances for all provider types but its name implies it filters by
availability; either rename it to getAllProviders to reflect current behavior or
change its implementation to filter using each provider's isAvailable() method
(call getProvider(type) for each AIProviderType and keep only those where
provider.isAvailable() is true). Update any call sites accordingly to use the
new name or expect the filtered list; reference getAvailableProviders,
getAllProviders, getProvider, AIProviderType, and isAvailable in your edits.

In `@src/utils/ai/tasks/Summarizer.ts`:
- Around line 15-18: The code in Summarizer.ts unsafely casts options.provider
with `as "cloud" | "local-hf" | "darwinkit"` which can accept invalid strings
and cause runtime failures in getProvider(); replace the cast with a runtime
validation step that checks options.provider against an explicit whitelist
(["cloud","local-hf","darwinkit"]) before calling config.set("summarize", ...),
handle invalid values by throwing a clear error or using a safe default, and
extract this check into a shared utility function (e.g., validateProvider or
assertValidProvider) that Transcriber, Translator and Summarizer can reuse so
all task classes perform the same provider validation.

In `@src/utils/ai/tasks/Transcriber.ts`:
- Around line 15-18: The code in Transcriber.ts unsafely asserts
options.provider to "cloud" | "local-hf" | "darwinkit"; replace this direct cast
with a validation step using a shared utility (e.g., create a validateProvider
or parseProvider function under src/utils/ai/) that checks options.provider
against the allowed set and returns a typed provider or throws/returns undefined
on invalid input; update Transcriber.ts to call that utility instead of the
inline cast (refer to the providerType variable and the config.set("transcribe",
...) call) and apply the same utility in Summarizer.ts and Translator.ts so
provider validation is centralized and type-safe.

In `@src/utils/ai/tasks/Translator.ts`:
- Around line 15-18: The code in Translator.ts unsafely casts options.provider
to "cloud" | "local-hf" | "darwinkit" before validating; add and use a shared
validator (e.g., isValidProvider or validateProvider) across the task classes
(Translator, Summarizer, and the third task) to check options.provider prior to
casting, and only call config.set("translate", { provider: providerType, model:
options.model }) when the validator confirms a known provider; if validation
fails, either fall back to a default provider or throw a clear error, ensuring
you reference options.provider and the config.set("translate", ...) call and
replace the direct cast with the validator-guarded value.

In `@src/utils/macos/notifications.ts`:
- Around line 102-103: Remove the redundant `continue` inside the catch block in
the loop in src/utils/macos/notifications.ts (the catch after the try that
includes the loop body) — simply delete the `continue;` statement so the catch
block either handles/logs the error or is empty, leaving control flow to
naturally proceed to the next iteration; update the catch in the function
containing that try/catch to no longer include `continue`.
- Around line 91-100: The code currently awaits proc.exited but never verifies
the exit status, so failing runs get treated as successes; update the candidate
probe in the try block (the block that calls Bun.spawn([candidate, "-help"]) and
then calls storage.setConfigValue and logger.debug) to check that await
proc.exited === 0 before caching the candidate and logging success, and
similarly update sendViaTerminalNotifier() to await the spawned process, confirm
its exit code equals 0 before returning true, and return false (and log an
error) on non-zero exits.

In `@src/utils/macos/tts.ts`:
- Around line 75-92: listVoicesStructured is discarding all but the first voice
per language by storing only the first-per-lang in the Map used to build the
result; instead, parse and retain the full VoiceInfo[] (e.g., collect into a
voices or voiceList array while iterating the lines) and separately build a
first-per-language Map<string, VoiceInfo> (keep the existing map variable or
create firstPerLang) strictly for language fallback logic; then return the full
parsed VoiceInfo[] (and if needed return or expose the fallback map separately),
making sure the return site that currently exposes the reduced map (the code
around where map is returned/exposed) returns the full list and not the reduced
map.
- Around line 122-127: The bug is that reading mute state for an app causes an
implicit write (setting config.appMute[app] = false) so a true globalMute gets
overridden after the first speak; update the code so reads do not persist
defaults: keep isMuted(config, app) as the authoritative read (using the
existence check on config.appMute), and remove or change any eager writes that
set config.appMute[app] = false inside the speak flow (or related functions) so
you only write to config.appMute when the user explicitly toggles mute/unmute.
Ensure any code that currently sets appMute[app] on first access (lines around
155-171) is refactored to treat undefined as “inherit global” and not persist a
false value.
- Around line 9-12: Remove the redundant visual section banners (e.g., the "//
============================================" and "// Types"/"//
Functions"/similar banner comment blocks) in the tts module; delete those
comment blocks that simply restate structure around the type and function
declarations (the ones noted at the ranges that include the type and function
declarations) so the file only contains the actual type declarations and
function implementations without the separator noise.
- Around line 130-139: The exported function setMute currently uses positional
optional args (setMute(muted, app?)); change its signature to accept a single
object parameter like setMute({ muted, app }) so callers and future options are
clearer and extensible; update the implementation to destructure the object and
keep the same logic (use getConfig(), mutate config.appMute[app] or
config.globalMute, then setConfig(config)), and update all call sites to pass an
object and any exported re-exports or spreads that reference setMute to use the
new object-parameter form.

---

Duplicate comments:
In `@src/zsh/features/notify.ts`:
- Around line 50-56: The comparison between active_app and term_app can be false
positives because their naming differs; add a small normalization step (e.g., a
helper function or inline mapping) to canonicalize both names before comparing
(referencing the variables active_app and term_app in this block). Normalize
common variants such as "Apple_Terminal" and "Terminal" to "Terminal",
"iTerm.app" and "iTerm2" to "iTerm", and "Code"/"Visual Studio Code" to the
TERM_PROGRAM value for VS Code (or vice versa), then compare the
normalized_active and normalized_term values so the check only notifies when the
user actually switched away. Ensure the normalization handles case and strips
".app" suffixes.
- Around line 77-93: The DEBUG trap is firing for the internal handlers and
clobbering __genesis_notify_cmd/__genesis_notify_start; modify the
preexec/precmd logic so internal handler invocations are ignored: add a guard in
__genesis_notify_preexec that returns immediately when $BASH_COMMAND matches the
internal handler names (__genesis_notify_precmd or __genesis_notify_preexec) or
when a short-lived guard flag (e.g. __genesis_notify_internal=1) is set, and
wrap __genesis_notify_precmd with that guard flag (set before running and unset
after) so the DEBUG trap won’t record the precmd call itself, thus preserving
the real command and start time.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d31cfac7-333a-42dc-9d6d-86e34638d0af

📥 Commits

Reviewing files that changed from the base of the PR and between 5f87acb and 80072dd.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • package.json
  • src/benchmark/index.ts
  • src/notify/index.ts
  • src/say/index.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/ai/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/tasks/Summarizer.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/utils/ai/tasks/Translator.ts
  • src/utils/ai/types.ts
  • src/utils/macos/index.ts
  • src/utils/macos/notifications.ts
  • src/utils/macos/tts.ts
  • src/zsh/features/index.ts
  • src/zsh/features/notify.ts
  • src/zsh/features/speak.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Do not add file-path comments as the first line of files
Do not add obvious comments that restate what the code already says
Never use one-line if statements; always use block form with braces
Include an empty line before if statements, unless the preceding line is a variable declaration used by that if
Include an empty line after closing } unless followed by else, catch, finally, or another }
Use object parameters for functions with 3+ parameters or optional parameters
Use positional parameters only for 1-2 required and obvious parameters such as estimateTokens(text) or resolve(base, path)
Prefer error: err over error: err instanceof Error ? err.message : String(err) when the error field accepts unknown

Files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Do not use as any for type casting; use proper type narrowing, type guards, or explicit interfaces instead
Use discriminant checks for union types (e.g., entity.className === "User")

Files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
src/utils/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place general-purpose utility functions in src/utils/ instead of inside tool directories

Files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
src/**/index.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/index.{ts,tsx}: Use commander for parsing command-line arguments with subcommands and options
Use @clack/prompts for interactive user experience in new tools; use @inquirer/prompts only for legacy tools
Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations
Use chalk for colored terminal output but strip ANSI codes for non-TTY environments
Use Bun.spawn() for executing external commands and handle stdout/stderr streams properly
Use Node.js path module for cross-platform path handling and resolve relative paths to absolute
Use Bun's native file APIs (Bun.write()) for better performance in file operations

Files:

  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/notify/index.ts
  • src/zsh/features/index.ts
  • src/utils/ai/index.ts
🧠 Learnings (18)
📓 Common learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.
📚 Learning: 2026-02-20T00:52:27.023Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 31
File: src/ask/utils/helpers.ts:3-3
Timestamp: 2026-02-20T00:52:27.023Z
Learning: In all TypeScript source files under src, prefer using picocolors for colored terminal output in new code. Picocolors is smaller and faster than chalk, so adopt it for CLI output coloring and avoid adding chalk in new code paths unless there is a compelling compatibility reason.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/utils/ai/tasks/Summarizer.ts
  • src/say/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/macos/index.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/macos/notifications.ts
  • src/zsh/features/speak.ts
  • src/notify/index.ts
  • src/utils/ai/types.ts
  • src/utils/ai/tasks/Translator.ts
  • src/zsh/features/index.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/AIConfig.ts
  • src/utils/ai/index.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/zsh/features/notify.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/macos/tts.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `clack/prompts` for interactive user experience in new tools; use `inquirer/prompts` only for legacy tools

Applied to files:

  • src/say/index.ts
  • src/benchmark/index.ts
  • package.json
  • src/notify/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `Bun.spawn()` for executing external commands and handle stdout/stderr streams properly

Applied to files:

  • src/say/index.ts
  • src/benchmark/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Support multiple output destinations (file, clipboard, stdout) and use `clipboardy` for clipboard operations

Applied to files:

  • src/say/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `commander` for parsing command-line arguments with subcommands and options

Applied to files:

  • src/say/index.ts
  • src/benchmark/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use Bun's native file APIs (`Bun.write()`) for better performance in file operations

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/utils/**/*.{ts,tsx} : Place general-purpose utility functions in `src/utils/` instead of inside tool directories

Applied to files:

  • src/utils/ai/index.ts
🧬 Code graph analysis (15)
src/utils/ai/tasks/Summarizer.ts (3)
src/utils/ai/index.ts (2)
  • Summarizer (11-11)
  • AIConfig (6-6)
src/utils/ai/types.ts (2)
  • AISummarizationProvider (54-56)
  • SummarizationResult (41-44)
src/utils/ai/providers/index.ts (1)
  • getProviderForTask (36-63)
src/say/index.ts (3)
src/utils/macos/tts.ts (6)
  • setMute (130-140)
  • speak (152-227)
  • getConfigForRead (114-116)
  • SayConfig (33-38)
  • getVoiceMap (58-99)
  • setConfig (110-112)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/prompts/clack/input.ts (1)
  • input (37-135)
src/benchmark/index.ts (3)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/json.ts (1)
  • SafeJSON (24-58)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/ai/providers/AICloudProvider.ts (2)
src/utils/ai/types.ts (8)
  • AITask (2-2)
  • AITranscriptionProvider (46-48)
  • AITranslationProvider (50-52)
  • AISummarizationProvider (54-56)
  • TranscribeOptions (11-15)
  • TranslateOptions (24-28)
  • TranslationResult (30-34)
  • SummarizationResult (41-44)
src/ask/audio/TranscriptionManager.ts (1)
  • TranscriptionManager (34-398)
src/utils/macos/notifications.ts (1)
src/utils/macos/index.ts (2)
  • NotificationOptions (21-21)
  • sendNotification (23-23)
src/zsh/features/speak.ts (1)
src/zsh/features/types.ts (1)
  • ZshFeature (1-6)
src/notify/index.ts (3)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/macos/index.ts (1)
  • sendNotification (23-23)
src/utils/macos/notifications.ts (1)
  • sendNotification (185-212)
src/utils/ai/tasks/Translator.ts (2)
src/utils/ai/types.ts (3)
  • AITranslationProvider (50-52)
  • TranslateOptions (24-28)
  • TranslationResult (30-34)
src/utils/ai/providers/index.ts (1)
  • getProviderForTask (36-63)
src/zsh/features/index.ts (2)
src/zsh/features/types.ts (1)
  • ZshFeature (1-6)
src/zsh/features/speak.ts (1)
  • speakFeature (3-9)
src/utils/ai/providers/AIDarwinKitProvider.ts (3)
src/utils/ai/types.ts (1)
  • AITask (2-2)
src/utils/ai/providers/index.ts (1)
  • AIDarwinKitProvider (79-79)
src/utils/macos/index.ts (4)
  • detectLanguage (11-11)
  • analyzeSentiment (9-9)
  • embedText (12-12)
  • textDistance (19-19)
src/utils/ai/AIConfig.ts (1)
src/utils/ai/types.ts (3)
  • TaskConfig (58-61)
  • AITask (2-2)
  • AIProviderType (1-1)
src/utils/ai/index.ts (4)
src/utils/ai/types.ts (4)
  • TranscribeOptions (11-15)
  • TranslateOptions (24-28)
  • TranslationResult (30-34)
  • SummarizationResult (41-44)
src/utils/ai/tasks/Transcriber.ts (1)
  • Transcriber (5-49)
src/utils/ai/tasks/Translator.ts (1)
  • Translator (5-49)
src/utils/ai/tasks/Summarizer.ts (1)
  • Summarizer (5-36)
src/utils/ai/tasks/Transcriber.ts (3)
src/utils/ai/index.ts (2)
  • Transcriber (9-9)
  • AIConfig (6-6)
src/utils/ai/types.ts (2)
  • AITranscriptionProvider (46-48)
  • TranscribeOptions (11-15)
src/utils/ai/providers/index.ts (1)
  • getProviderForTask (36-63)
src/utils/ai/providers/AILocalProvider.ts (1)
src/utils/ai/types.ts (8)
  • AITask (2-2)
  • AITranscriptionProvider (46-48)
  • AITranslationProvider (50-52)
  • AISummarizationProvider (54-56)
  • TranscribeOptions (11-15)
  • TranslateOptions (24-28)
  • TranslationResult (30-34)
  • SummarizationResult (41-44)
src/utils/macos/tts.ts (2)
src/utils/macos/index.ts (10)
  • VoiceInfo (48-48)
  • SpeakOptions (48-48)
  • SayConfig (48-48)
  • getVoiceMap (52-52)
  • setConfig (55-55)
  • getConfigForRead (51-51)
  • setMute (56-56)
  • speak (57-57)
  • listVoices (53-53)
  • listVoicesStructured (54-54)
src/telegram/lib/TelegramMessage.ts (1)
  • text (10-12)
🪛 GitHub Actions: CI
src/say/index.ts

[error] 247-247: lint/suspicious/noGlobalIsNan: isNaN is unsafe. Use Number.isNaN instead.


[error] 3-3: assist/source/organizeImports: The imports and exports are not sorted. Safe fix: Organize Imports (Biome).

src/utils/ai/ModelManager.ts

[error] 1-1: assist/source/organizeImports: The imports and exports are not sorted. Safe fix: Organize Imports (Biome).

src/utils/macos/notifications.ts

[error] 103-103: lint/complexity/noUselessContinue: Unnecessary continue statement. Safe fix: Delete the unnecessary continue statement.

src/utils/macos/tts.ts

[error] 1-1: assist/source/organizeImports: The imports and exports are not sorted. Safe fix: Organize Imports (Biome).

🔇 Additional comments (23)
package.json (1)

60-60: Dependencies are valid and secure; note platform requirements for native bindings.

The three new dependencies are all current versions with no known security vulnerabilities. The @huggingface/transformers and youtube-transcript packages integrate cleanly. However, onnxruntime-node includes native bindings—ensure your Node.js version and target platforms (macOS ARM64/x64, Windows, Linux) are compatible before deployment.

src/utils/ai/tasks/Summarizer.ts (1)

29-31: LGTM!

Clean delegation pattern. The method signature matches the AISummarizationProvider.summarize interface correctly.

src/utils/ai/AIConfig.ts (2)

32-37: LGTM!

Clean factory pattern with proper async loading. The fallback to defaults on line 35 and per-task fallback on line 40 provides good defensive behavior.


43-46: LGTM!

The set method correctly merges partial config with existing values.

src/utils/ai/index.ts (1)

13-44: LGTM!

The AI namespace provides a clean, consistent API surface. The try/finally pattern in each convenience method ensures proper resource cleanup even if the task throws.

src/utils/ai/providers/AIDarwinKitProvider.ts (1)

50-54: LGTM!

Good defensive handling of empty categories array with optional chaining and nullish coalescing.

src/utils/ai/providers/index.ts (1)

36-63: LGTM!

The provider resolution logic with fallback is well-structured. The fallback order is clearly documented, and the error message is informative.

src/utils/ai/ModelManager.ts (1)

76-83: Age check uses modification time, not access time.

Using mtimeMs means a model that was downloaded long ago but recently used will be considered "old" and removed. If the intent is to clean up unused models, consider using atimeMs (access time) instead, though note that atime may be disabled on some filesystems.

Verify whether the intent is to remove models that haven't been modified recently (current behavior) or models that haven't been accessed/used recently (would need atimeMs).

src/utils/ai/tasks/Translator.ts (1)

29-44: LGTM!

The auto-detection logic is a nice enhancement. Good use of spreading to avoid mutating the original options object, and the silent catch with explanatory comment is appropriate for this optional feature.

src/utils/ai/tasks/Transcriber.ts (1)

29-44: LGTM!

Clean file-to-buffer conversion logic. Using Bun's native file APIs aligns with the coding guidelines.

src/utils/ai/providers/AICloudProvider.ts (6)

1-16: LGTM!

Clean imports and type organization. The separation of type-only imports from runtime imports is good practice.


18-45: LGTM!

Constants are well-organized, and the class structure properly implements all three provider interfaces with a clean constructor.


47-71: LGTM!

The transcription flow is well-structured with proper temp file cleanup in the finally block. The dynamic import of unlinkSync keeps the module lightweight.


94-113: LGTM!

The summarization implementation is clean with proper optional parameter handling for maxLength.


115-134: LGTM!

The provider fallback chain (groq → openrouter → openai) with clear error messaging is well-implemented.


136-163: Packages are properly declared in package.json.

The @ai-sdk/groq and @ai-sdk/openai packages imported in this code are already included in the project's dependencies, along with the base ai package.

src/utils/ai/types.ts (3)

1-9: LGTM!

Well-designed base types. The AITask union includes forward-looking tasks ("classify", "embed", "sentiment") even though current providers only support a subset, which is good for extensibility.


11-44: LGTM!

Clean option/result interfaces with appropriate optional fields. The TranscriptionResult.segments array for timestamp data is well-typed.


46-61: LGTM!

Provider interfaces properly extend the base AIProvider and define task-specific method signatures. The TaskConfig interface provides a clean configuration shape.

src/utils/ai/providers/AILocalProvider.ts (4)

1-20: LGTM!

Clean imports and the PipelineInstance type provides a reasonable abstraction for the HuggingFace pipeline interface.


74-86: LGTM!

The summarization implementation is clean with proper optional parameter handling.


88-103: LGTM!

Good pipeline caching strategy using task:model composite keys. The dtype: "q4" quantization is a reasonable default for local inference.


105-112: LGTM!

The dispose method properly iterates all cached pipelines and clears the map. Silently catching errors during cleanup is acceptable here.

genesiscz added a commit that referenced this pull request Mar 17, 2026
- t14: use `typeset` instead of `local` at top-level in dotdotdot.ts
- t17: guard DEBUG trap against internal hook functions in notify
- t16: normalize TERM_PROGRAM to match AppleScript app names
- t20/t21: add negatable --no-ignore-dnd/--no-say flags, fix help()
- t30: decode WAV audio before passing to Whisper pipeline
- t31: wrap translation model lookup in try/catch with helpful error
- t32: rename getAvailableProviders() to getAllProviders()
- t18: remove hardcoded /opt/homebrew path in benchmark
- t22: use parseAsync() in say CLI for proper error handling
- t23: use full voice list instead of deduplicated map
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ai/index.ts`:
- Around line 529-534: The current flow writes interactive input to a
predictable /tmp file (tmpPath) via Bun.write and calls cmdSummarize(tmpPath),
leaving sensitive data on disk and using a POSIX-only path; update the handler
around withCancel and cmdSummarize to either pass the captured text directly
into cmdSummarize (avoid creating tmp files) or, if a file is required, create a
platform-safe temp file using path.join(os.tmpdir() or tmpdir())/path.resolve
and ensure creation/deletion happens inside a try/finally so the file is removed
in cleanup; replace direct use of "/tmp/..." and Bun.write with these safer
approaches and adjust cmdSummarize call signature or wrapper accordingly.
- Around line 113-117: The summarize command currently ignores the provider
flag; update the call to AI.summarize to forward the provider (and other flags)
from the SummarizeFlags object instead of only passing maxLength (e.g., change
AI.summarize(input, { maxLength }) to AI.summarize(input, { maxLength:
opts.maxLength, provider: opts.provider, clipboard: opts.clipboard })) so the
provider option is honored, or alternatively remove the provider option from the
CLI registration (the code around the SummarizeFlags interface and the CLI
option block that defines the provider) if you prefer not to support per-command
providers.
- Around line 233-236: The classify subcommand currently ignores the provider
flag (ClassifyFlags.provider) and always calls classifyText(), so update the
dispatch logic where opts is consumed to read opts.provider and branch
accordingly: if provider is the macOS implementation (e.g., "macos" or "darwin")
call classifyText(), if it's another supported provider call the corresponding
provider-specific function (e.g., classifyWithOpenAI /
classifyWith<ProviderName>), and if unspecified use a sensible default; also
validate unknown provider values and return a clear error. Ensure
ClassifyFlags.provider is actually referenced in the dispatch and that
provider-specific functions are invoked instead of unconditionally calling
classifyText().

In `@src/macos/commands/voice-memos/index.ts`:
- Around line 17-65: The command file duplicates handler logic; extract the
shared voice-memos action functions and helpers (listAction, playAction,
exportAction, transcribeAction, searchAction, interactiveMode, and any
supporting helpers/types) into a single shared module (e.g., voice-memos
handlers), export them, and update registerVoiceMemosCommand to only import and
reference those functions; remove duplicate implementations from
src/macos/commands/voice-memos/index.ts (and ensure the original
src/voice-memos/index.ts also imports the same shared module) so this file only
performs command registration and wiring.

In `@src/macos/index.ts`:
- Around line 13-16: Update the Voice Memos usage header to reflect the actual
registered subcommands for the "voice-memos" command: include "export",
"search", and the "transcribe [id]" usage with the "--all" flag (e.g., "tools
macos voice-memos transcribe [id] --all"), so the usage block in
src/macos/index.ts matches the exported command surface (voice-memos export,
voice-memos search, voice-memos transcribe) and avoids misleading CLI help text.

In `@src/timer/index.ts`:
- Around line 435-441: In the validate(value) function (the validator that
converts value to n with const n = Number(value)), replace the use of the global
isNaN with Number.isNaN to perform stricter type-checked NaN detection; update
the condition to use Number.isNaN(n) (keeping the rest of the checks n < 1 ||
!Number.isInteger(n)) and return the same error string "Must be a positive
integer" when the condition is met.
- Around line 317-322: The code uses the global isNaN when parsing idOrIndex
into idx (const idx = Number(idOrIndex); if (!isNaN(idx) && idx >= 1 && idx <=
alive.length) ...), which the linter flags as unsafe; replace the check with
Number.isNaN to perform strict NaN detection (i.e., change the condition to use
Number.isNaN(idx) appropriately) so the branch that picks target = alive[idx -
1] only executes when idx is a valid number within range.

In `@src/transcribe/index.ts`:
- Around line 51-79: toSRT and toVTT currently emit raw untimed text when
TranscriptionResult has no segments, producing invalid SRT/VTT files; update the
toSRT(result: TranscriptionResult) and toVTT(result: TranscriptionResult)
functions to either (a) synthesize a single timed cue using result.duration
(e.g., 00:00:00,000 --> duration for SRT and 00:00:00.000 --> duration for VTT)
and include the trimmed result.text, or (b) throw/reject when result.segments is
missing so callers know timing is required—choose one behavior and apply it
consistently for both functions, making sure to reference result.segments and
result.duration in the implementation.
- Around line 34-40: The formatTimestamp function can produce ms = 1000 when
rounding the fractional seconds; fix formatTimestamp to compute from total
milliseconds instead of rounding the fractional part: compute const totalMs =
Math.round(seconds * 1000), then derive h = Math.floor(totalMs / 3_600_000), m =
Math.floor((totalMs % 3_600_000) / 60_000), s = Math.floor((totalMs % 60_000) /
1000), ms = totalMs % 1000 and use pad/pad3 to format the parts with the
provided separator, ensuring no ms overflow like 1000 occurs.
- Around line 320-340: The CLI registers an async action on the Command instance
"program" (the .action handler that calls interactiveMode() or
runTranscription()), but calls program.parse() which doesn't await async
handlers; replace the final call to program.parse() with program.parseAsync() so
Commander will await the async action handler's Promise and allow
interactiveMode/runTranscription to finish properly—update the call site where
"program.parse()" appears to "program.parseAsync()".

In `@src/utils/macos/voice-memos.ts`:
- Around line 209-212: The parseTsrpPayload function currently uses the lenient
SafeJSON.parse(json); update it to parse embedded tsrp payloads in strict mode
by calling SafeJSON.parse(json, { strict: true }) inside parseTsrpPayload so
malformed transcript JSON is not silently accepted; keep the existing try/catch
but ensure the strict option is passed to SafeJSON.parse to enforce strict
validation of the transcription payload.

In `@src/voice-memos/index.ts`:
- Around line 53-59: transcribeAction currently ignores opts.force and calls
transcribeOne(id) without forwarding the force flag, and transcribeOne only
returns embedded tsrp text (no AI fallback); update transcribeAction to pass
opts.force into transcribeOne, change transcribeOne signature to accept a
force:boolean parameter (default false), and implement logic in transcribeOne to
attempt the AI fallback transcription when either no tsrp transcript exists or
force===true (while still preferring tsrp when force is false); also update any
other callers (e.g., the batch transcribe path referenced around the other
block) to pass the force flag so the CLI --force behavior and advertised AI
fallback are honored.

In `@src/youtube/commands/transcribe.ts`:
- Around line 125-136: The formatTimestamp function can produce ms==1000 which
yields invalid timestamps like "00:00:59.1000"; fix it by computing from total
milliseconds rather than fractional seconds: e.g. compute totalMs =
Math.round(seconds * 1000) and then derive hours, minutes, seconds, and ms via
integer division/modulo so any ms overflow is carried into
seconds/minutes/hours; update formatTimestamp to use this totalMs approach
(refer to the function name formatTimestamp and the local variables
hours/minutes/secs/ms) so formatted strings always produce ms in the 0–999
range.
- Around line 261-275: The current CaptionResult construction discards timing by
hardcoding segments: [] after calling transcriptionManager.transcribeAudio;
update the result to use the segments returned by
transcriptionManager.transcribeAudio (e.g., transcription.segments) when
present, and only fall back to synthesizing a single cue (one segment with
start/end and the full transcription.text) or erroring when the requested
subtitle format (format === 'srt' or 'vtt') requires timing; ensure you update
the result variable used by formatOutput so subtitle outputs keep timing
information (referencing transcriptionManager.transcribeAudio, the returned
transcription object, CaptionResult/result, and formatOutput).

In `@src/youtube/index.ts`:
- Around line 4-10: The entrypoint uses program.parse() but the transcribe
subcommand action is async; replace the synchronous call with the async variant
by calling program.parseAsync() and awaiting its result (or prefixing with void
if not awaiting) so Commander runs async action handlers correctly; update the
call near the `program` initialization where `createTranscribeCommand()` is
added to use `parseAsync()` instead of `parse()`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c1002020-ec83-4af5-ae9a-8041368e84c8

📥 Commits

Reviewing files that changed from the base of the PR and between 80072dd and 48d086b.

📒 Files selected for processing (10)
  • src/ai/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/macos/index.ts
  • src/timer/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
  • src/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/youtube/index.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Do not add file-path comments as the first line of files
Do not add obvious comments that restate what the code already says
Never use one-line if statements; always use block form with braces
Include an empty line before if statements, unless the preceding line is a variable declaration used by that if
Include an empty line after closing } unless followed by else, catch, finally, or another }
Use object parameters for functions with 3+ parameters or optional parameters
Use positional parameters only for 1-2 required and obvious parameters such as estimateTokens(text) or resolve(base, path)
Prefer error: err over error: err instanceof Error ? err.message : String(err) when the error field accepts unknown

Files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Do not use as any for type casting; use proper type narrowing, type guards, or explicit interfaces instead
Use discriminant checks for union types (e.g., entity.className === "User")

Files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
src/**/index.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/index.{ts,tsx}: Use commander for parsing command-line arguments with subcommands and options
Use @clack/prompts for interactive user experience in new tools; use @inquirer/prompts only for legacy tools
Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations
Use chalk for colored terminal output but strip ANSI codes for non-TTY environments
Use Bun.spawn() for executing external commands and handle stdout/stderr streams properly
Use Node.js path module for cross-platform path handling and resolve relative paths to absolute
Use Bun's native file APIs (Bun.write()) for better performance in file operations

Files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
src/utils/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place general-purpose utility functions in src/utils/ instead of inside tool directories

Files:

  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
🧠 Learnings (16)
📓 Common learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `commander` for parsing command-line arguments with subcommands and options

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/ai/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Support multiple output destinations (file, clipboard, stdout) and use `clipboardy` for clipboard operations

Applied to files:

  • src/youtube/index.ts
  • src/transcribe/index.ts
📚 Learning: 2026-03-15T15:54:08.510Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 105
File: src/ask/index.ts:66-74
Timestamp: 2026-03-15T15:54:08.510Z
Learning: In genesiscz/GenesisTools, manual argv._[0] dispatch in src/ask/index.ts (and similar entry-point files) is intentional for lazy loading: subcommand modules (e.g. ask/commands/configure) are imported via dynamic import() inside the dispatch branch rather than registered as Commander subcommands. This avoids eagerly loading all subcommand modules at startup. Do NOT flag this pattern as a violation of the "use Commander for subcommands" guideline; the lazy-loading benefit outweighs the missing Commander help/error-handling for these entry points.

Applied to files:

  • src/youtube/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `clack/prompts` for interactive user experience in new tools; use `inquirer/prompts` only for legacy tools

Applied to files:

  • src/youtube/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
📚 Learning: 2026-02-20T00:52:27.023Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 31
File: src/ask/utils/helpers.ts:3-3
Timestamp: 2026-02-20T00:52:27.023Z
Learning: In all TypeScript source files under src, prefer using picocolors for colored terminal output in new code. Picocolors is smaller and faster than chalk, so adopt it for CLI output coloring and avoid adding chalk in new code paths unless there is a compelling compatibility reason.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/youtube/index.ts
  • src/macos/index.ts
  • src/voice-memos/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/youtube/commands/transcribe.ts
  • src/timer/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/macos/index.ts
  • src/utils/macos/voice-memos.ts
🧬 Code graph analysis (8)
src/youtube/index.ts (1)
src/youtube/commands/transcribe.ts (1)
  • createTranscribeCommand (296-310)
src/macos/index.ts (1)
src/macos/commands/voice-memos/index.ts (1)
  • registerVoiceMemosCommand (17-65)
src/voice-memos/index.ts (2)
src/utils/macos/voice-memos.ts (6)
  • VoiceMemo (10-18)
  • getMemo (398-419)
  • listMemos (378-396)
  • extractTranscript (337-372)
  • searchMemos (421-441)
  • VoiceMemosError (54-62)
src/utils/table.ts (1)
  • formatTable (13-47)
src/macos/commands/voice-memos/index.ts (1)
src/utils/table.ts (1)
  • formatTable (13-47)
src/youtube/commands/transcribe.ts (5)
src/utils/ai/types.ts (1)
  • TranscribeOptions (11-15)
src/Internal/LoggerLib/Logger.ts (1)
  • format (133-154)
src/utils/json.ts (1)
  • SafeJSON (24-58)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • spinner (50-52)
src/ask/audio/TranscriptionManager.ts (1)
  • transcriptionManager (401-401)
src/timer/index.ts (3)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/format.ts (1)
  • parseDuration (123-159)
src/ai/index.ts (5)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/ai/index.ts (1)
  • AI (13-44)
src/utils/clipboard.ts (2)
  • copyToClipboard (4-14)
  • readFromClipboard (16-18)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/ai/types.ts (1)
  • AITask (2-2)
src/utils/macos/voice-memos.ts (2)
src/utils/macos/index.ts (9)
  • VoiceMemo (77-77)
  • TranscriptSegment (77-77)
  • TranscriptionResult (77-77)
  • VoiceMemosError (85-85)
  • hasTranscript (82-82)
  • extractTranscript (80-80)
  • listMemos (83-83)
  • getMemo (81-81)
  • searchMemos (84-84)
src/utils/json.ts (1)
  • SafeJSON (24-58)
🪛 GitHub Actions: CI
src/youtube/commands/transcribe.ts

[error] 245-245: lint/style/useTemplate: Use template literals instead of string concatenation. Unsafe fix suggested by Biome.

src/timer/index.ts

[error] 319-319: lint/suspicious/noGlobalIsNan: isNaN is unsafe. Use Number.isNaN instead.


[error] 438-438: lint/suspicious/noGlobalIsNan: isNaN usage detected. Use Number.isNaN instead.

src/ai/index.ts

[error] 372-372: lint/style/useTemplate: Use template literals instead of string concatenation. Unsafe fix suggested by Biome.

🔇 Additional comments (11)
src/timer/index.ts (10)

1-31: LGTM!

Imports and type definitions are well-structured. Good use of picocolors as per project conventions, and types clearly capture timer state.


37-67: LGTM!

Storage operations properly use withConfigLock for atomic writes, preventing race conditions when adding/removing timers. The ID generation is adequate for a local CLI tool.


73-88: LGTM!

Countdown formatting is correct. Using Math.ceil ensures the timer shows "00:01" until the final second completes.


90-135: LGTM!

The foreground timer implementation correctly handles multiple cycles with live terminal updates. The 250ms polling interval provides smooth countdown display.


137-167: LGTM!

Good use of Promise.allSettled for concurrent completion actions—ensures one failure doesn't block the other. Path resolution correctly navigates to the tools script.


173-244: LGTM!

Background timer implementation correctly uses detached processes with child.unref(). The internal __bg-run__ handler properly executes cycles and cleans up on completion.


259-300: LGTM!

listTimers correctly filters stale entries by checking process liveness and opportunistically cleans up the storage. The table output is well-formatted.


302-360: LGTM!

cancelTimer provides good UX with support for ID lookup, 1-based index, and interactive selection when multiple timers exist. The SIGTERM signal allows graceful termination.


366-461: LGTM!

The interactive flow provides an excellent user experience with clear prompts, sensible defaults, and proper validation. Good use of @clack/prompts as per project guidelines.


467-539: LGTM!

CLI setup correctly uses Commander with a main command and subcommands. The __bg-run__ internal flag handling in main() cleanly separates the background runner from normal CLI parsing.

src/macos/index.ts (1)

25-25: Voice Memos command wiring looks correct.

Import and registration are consistent with the existing commander-based subcommand setup and cleanly integrate the new voice-memos surface.

As per coding guidelines "Use commander for parsing command-line arguments with subcommands and options".

Also applies to: 38-38

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/utils/ai/providers/AILocalProvider.ts (1)

117-139: ⚠️ Potential issue | 🔴 Critical

Do not reinterpret unsupported audio bytes as PCM.

The RIFF handling is a good start, but Lines 117-139 still only support 16-bit PCM WAV with the data chunk assumed at byte 44. The hard-coded offset on Line 122 breaks valid WAVs with metadata chunks, and MP3/M4A still fall through to new Float32Array(audio.buffer, ...), which reinterprets encoded bytes as samples and corrupts transcription. At minimum, throw here so callers can fall back cleanly; for full support, walk the RIFF chunks or use a real decoder.

♻️ Minimal safe guard
-        // For non-WAV formats, try using the raw buffer as Float32
-        // (caller should convert to WAV first for reliable results)
-        return new Float32Array(audio.buffer, audio.byteOffset, audio.byteLength / 4);
+        throw new Error(
+            "Unsupported audio format for local transcription. Please provide 16-bit PCM WAV or use a provider that can decode the input format."
+        );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/ai/providers/AILocalProvider.ts` around lines 117 - 139, The
decodeAudio function currently mis-parses WAVs with non-standard chunk offsets
and falls back to reinterpreting arbitrary encoded bytes as Float32, corrupting
audio; update decodeAudio to validate and parse RIFF chunks (or use a proper
decoder) and to only accept 16-bit PCM after locating the "data" chunk instead
of assuming offset 44, and if the buffer is not a supported PCM WAV (e.g.,
missing "data" chunk, unsupported bitsPerSample, or non-PCM format) throw a
clear error so callers can handle fallback logic rather than reinterpreting
bytes as Float32.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/ai/providers/AILocalProvider.ts`:
- Around line 100-114: getPipeline currently checks this.pipelines after await
pipeline(...), so concurrent callers can trigger duplicate loads; change it to
memoize the in-flight load by storing a Promise for the PipelineInstance in
this.pipelines under the computed key before awaiting the actual import/result
(use the existing key and this.pipelines map), have all callers await that
stored Promise, and if the Promise rejects remove the key so future calls can
retry; ensure types around pipeline import/cast and stored
Promise<PipelineInstance> are adjusted accordingly so getPipeline still returns
a Promise<PipelineInstance>.
- Around line 60-63: The translate method in AILocalProvider currently defaults
TranslateOptions.from to "en", causing wrong model selection for non-English
input; update the translate(text: string, options: TranslateOptions):
Promise<TranslationResult> implementation to not assume "en" — either require
options.from when using the local-hf pathway (throw a clear error if missing) or
run language detection on the input text before constructing the model name
(used to build `Helsinki-NLP/opus-mt-${from}-${to}`); adjust logic around the
`model` variable and any branches that depend on `from` so the correct model is
chosen or an explicit error is returned when `from` is absent.

In `@src/zsh/features/dotdotdot.ts`:
- Around line 28-44: The function __genesis_dotdotdot_collapse_buf
unconditionally sets CURSOR to ${`#BUFFER`}, which forces the cursor to EOL and
breaks mid-buffer completion; instead preserve the original cursor by performing
replacements on LBUFFER and RBUFFER (recompute BUFFER only from LBUFFER+RBUFFER
if needed) or restore CURSOR after modifications rather than setting it to
end-of-line. Update __genesis_dotdotdot_collapse_buf to apply the same
chain/ndots replacements to LBUFFER (and/or RBUFFER) so the cursor position
remains intact, avoid assigning CURSOR=${`#BUFFER`}, and ensure CURSOR is left
unchanged or restored from the original value saved by
__genesis_dotdotdot_saved_complete.

---

Duplicate comments:
In `@src/utils/ai/providers/AILocalProvider.ts`:
- Around line 117-139: The decodeAudio function currently mis-parses WAVs with
non-standard chunk offsets and falls back to reinterpreting arbitrary encoded
bytes as Float32, corrupting audio; update decodeAudio to validate and parse
RIFF chunks (or use a proper decoder) and to only accept 16-bit PCM after
locating the "data" chunk instead of assuming offset 44, and if the buffer is
not a supported PCM WAV (e.g., missing "data" chunk, unsupported bitsPerSample,
or non-PCM format) throw a clear error so callers can handle fallback logic
rather than reinterpreting bytes as Float32.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 725a663a-c967-4870-b0b2-f61a6ae8982b

📥 Commits

Reviewing files that changed from the base of the PR and between 48d086b and 43a053c.

📒 Files selected for processing (7)
  • src/benchmark/index.ts
  • src/notify/index.ts
  • src/say/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/providers/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/zsh/features/notify.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Do not add file-path comments as the first line of files
Do not add obvious comments that restate what the code already says
Never use one-line if statements; always use block form with braces
Include an empty line before if statements, unless the preceding line is a variable declaration used by that if
Include an empty line after closing } unless followed by else, catch, finally, or another }
Use object parameters for functions with 3+ parameters or optional parameters
Use positional parameters only for 1-2 required and obvious parameters such as estimateTokens(text) or resolve(base, path)
Prefer error: err over error: err instanceof Error ? err.message : String(err) when the error field accepts unknown

Files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Do not use as any for type casting; use proper type narrowing, type guards, or explicit interfaces instead
Use discriminant checks for union types (e.g., entity.className === "User")

Files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
src/**/index.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/index.{ts,tsx}: Use commander for parsing command-line arguments with subcommands and options
Use @clack/prompts for interactive user experience in new tools; use @inquirer/prompts only for legacy tools
Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations
Use chalk for colored terminal output but strip ANSI codes for non-TTY environments
Use Bun.spawn() for executing external commands and handle stdout/stderr streams properly
Use Node.js path module for cross-platform path handling and resolve relative paths to absolute
Use Bun's native file APIs (Bun.write()) for better performance in file operations

Files:

  • src/notify/index.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/say/index.ts
src/utils/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place general-purpose utility functions in src/utils/ instead of inside tool directories

Files:

  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
🧠 Learnings (40)
📓 Common learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:54.235Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-16-ToolsEnhancements.md:131-131
Timestamp: 2026-03-17T01:47:51.462Z
Learning: In the genesiscz/GenesisTools repository, files under `.claude/plans/` (e.g., `.claude/plans/2026-03-16-ToolsEnhancements.md`) are implementation plan documents, not production code. Do not flag implementation-level issues (e.g., filename uniqueness, overwrite risks, edge cases) in these files, as such concerns are addressed in the actual implementation. Treat these documents as design artifacts only.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-16-AITools.md:112-112
Timestamp: 2026-03-17T01:47:52.068Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), files under `.claude/plans/` (e.g., `.claude/plans/2026-03-16-AITools.md`) are implementation plan/design documents, not production code. Do not flag code quality issues (e.g., magic numbers, missing named constants, style concerns) in code snippets within these plan documents, as they are illustrative only and the actual implementation addresses such issues.
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `clack/prompts` for interactive user experience in new tools; use `inquirer/prompts` only for legacy tools

Applied to files:

  • src/notify/index.ts
  • src/benchmark/index.ts
  • src/say/index.ts
📚 Learning: 2026-03-17T01:32:51.397Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:122-127
Timestamp: 2026-03-17T01:32:51.397Z
Learning: In genesiscz/GenesisTools, mute/config state (SayConfig, isMuted, setMute, getConfig, getConfigForRead) belongs at the CLI layer (src/say/) rather than in the TTS utility layer (src/utils/macos/tts.ts). The tts.ts file should remain a pure macOS TTS primitive (getVoiceMap, speak, listVoices, listVoicesStructured) without any application-level config or mute logic. The `app` field in SpeakOptions should also be removed since it only exists to support per-app muting.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-15T15:54:08.510Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 105
File: src/ask/index.ts:66-74
Timestamp: 2026-03-15T15:54:08.510Z
Learning: In genesiscz/GenesisTools, manual argv._[0] dispatch in src/ask/index.ts (and similar entry-point files) is intentional for lazy loading: subcommand modules (e.g. ask/commands/configure) are imported via dynamic import() inside the dispatch branch rather than registered as Commander subcommands. This avoids eagerly loading all subcommand modules at startup. Do NOT flag this pattern as a violation of the "use Commander for subcommands" guideline; the lazy-loading benefit outweighs the missing Commander help/error-handling for these entry points.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:38.771Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/utils/claude/auth.ts:0-0
Timestamp: 2026-03-12T01:26:38.771Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument can be either a reviver function or a `ParseOptions` object (e.g., `{ strict: true }`). There is no separate third `options` argument. Use `SafeJSON.parse(text, { strict: true })` at external/API/subprocess/JSONL/cache-file boundaries so malformed data fails fast with strict RFC 8259 validation. Use the lenient default (no second arg) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/notify/index.ts
  • src/say/index.ts
📚 Learning: 2026-03-09T13:39:43.059Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-17T01:30:56.939Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:130-139
Timestamp: 2026-03-17T01:30:56.939Z
Learning: In genesiscz/GenesisTools, do not suggest converting two-argument functions with an optional second parameter (for example setMute(muted: boolean, app?: string)) to an object-parameter form. The project prefers simple positional parameters for short utility functions, even when an optional argument is present. The object-parameter guideline should only apply when a function has 3 or more parameters.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-11T17:12:27.285Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/clarity/ui/src/routes/settings.tsx:1-1
Timestamp: 2026-03-11T17:12:27.285Z
Learning: In genesiscz/GenesisTools, `SafeJSON` in `src/utils/json.ts` intentionally uses `comment-json.parse` for parsing (to handle `//` comments, trailing commas, unquoted keys) but `JSON.stringify` (native) for stringification. Do not flag `SafeJSON.stringify` usages as unnecessary — the intent is a unified drop-in for `JSON` that parses leniently but serialises as standard JSON.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
📚 Learning: 2026-02-20T00:52:27.023Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 31
File: src/ask/utils/helpers.ts:3-3
Timestamp: 2026-02-20T00:52:27.023Z
Learning: In all TypeScript source files under src, prefer using picocolors for colored terminal output in new code. Picocolors is smaller and faster than chalk, so adopt it for CLI output coloring and avoid adding chalk in new code paths unless there is a compelling compatibility reason.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/notify/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-17T01:47:54.736Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:148-148
Timestamp: 2026-03-17T01:47:54.736Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation plan documents (design/intent descriptions), not production code. Do not raise code-quality, robustness, or implementation-detail review comments (e.g., race conditions, error handling, temp file strategy) on these plan files. Such concerns are addressed in the actual implementation files (e.g., src/**/*.ts).

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/index.ts
  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-17T01:47:54.235Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:54.235Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.

Applied to files:

  • src/utils/ai/providers/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `Bun.spawn()` for executing external commands and handle stdout/stderr streams properly

Applied to files:

  • src/benchmark/index.ts
  • src/say/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use Bun's native file APIs (`Bun.write()`) for better performance in file operations

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `commander` for parsing command-line arguments with subcommands and options

Applied to files:

  • src/benchmark/index.ts
  • src/say/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: TypeScript is configured in strict mode with ES modules and Bun executes TypeScript directly without a build step

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-12T01:26:31.776Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/daemon/lib/log-reader.ts:0-0
Timestamp: 2026-03-12T01:26:31.776Z
Learning: In genesiscz/GenesisTools, enforce SafeJSON.parse strict mode at all non-config boundaries: external API responses, JSONL line parsers, cache files, and subprocess stdout. Use `SafeJSON.parse(input, { strict: true })` (2-arg form). Fall back to lenient default (no second arg) only for user-authored config files that may contain comments or trailing commas.

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-12T01:26:36.117Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:36.117Z
Learning: In genesiscz/GenesisTools, enforce `SafeJSON.parse(text, { strict: true })` (2-arg form) at all non-config boundaries: API responses, JSONL lines, machine-generated cache files (e.g. `workitem-*.json`), and subprocess stdout. Reserve lenient/default parsing (no second arg) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/benchmark/index.ts
  • src/say/index.ts
📚 Learning: 2026-03-12T01:26:16.671Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/cache.ts:0-0
Timestamp: 2026-03-12T01:26:16.671Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second parameter is a union of reviver function or options object. When `{ strict: true }` is passed as the second argument, it delegates to native `JSON.parse` instead of `comment-json`'s lenient parser. Use `SafeJSON.parse(text, { strict: true })` at remote/third-party API response boundaries, JSONL parsing points, machine-written cache files, and subprocess output that need strict RFC 8259 validation. The lenient default (no second arg or `null`) is only appropriate for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-12T01:26:26.145Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/macos/lib/mail/jxa.ts:0-0
Timestamp: 2026-03-12T01:26:26.145Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. To enable strict RFC 8259 validation (delegating to native `JSON.parse`), pass `{ strict: true }` as the second argument: `SafeJSON.parse(text, { strict: true })`. There is NO 3-argument overload — do NOT suggest `SafeJSON.parse(text, null, { strict: true })`. Use strict mode at all remote/third-party API response boundaries, JSONL parsing points, subprocess stdout, and cache files; reserve lenient default (no second arg) only for user-authored config files that may contain comments or trailing commas.

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-12T01:26:12.215Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/api.ts:0-0
Timestamp: 2026-03-12T01:26:12.215Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument can be either a reviver function or a `ParseOptions` object (e.g., `{ strict: true }`). When `{ strict: true }` is passed, it delegates to native `JSON.parse` instead of `comment-json`'s lenient parser. Use `SafeJSON.parse(text, { strict: true })` (NOT the 3-arg form with `null`) at remote/third-party API response boundaries, JSONL parsing points, cache files, and subprocess output that need strict RFC 8259 validation. Fall back to lenient default (no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-12T01:26:47.022Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/timelog/import.ts:0-0
Timestamp: 2026-03-12T01:26:47.022Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts exactly TWO arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument is overloaded: pass a function for a reviver, or pass `{ strict: true }` / `{ jsonl: true }` as a `ParseOptions` object to delegate to native `JSON.parse`. The correct strict-mode call is `SafeJSON.parse(text, { strict: true })` — NOT the 3-arg form `SafeJSON.parse(text, null, { strict: true })`. Use strict mode at remote/third-party API response boundaries, JSONL parsing points, cache files, and subprocess output. Reserve the lenient default (no options) for user-authored config files that may contain comments or trailing commas.

Applied to files:

  • src/benchmark/index.ts
  • src/say/index.ts
📚 Learning: 2026-03-12T01:26:30.872Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/hold-ai/client.ts:0-0
Timestamp: 2026-03-12T01:26:30.872Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument can be either a reviver function or a `ParseOptions` object (e.g., `{ strict: true }`). To enable strict RFC 8259 validation (delegating to native `JSON.parse`), use `SafeJSON.parse(text, { strict: true })` — there is no separate third `options` parameter. Apply `{ strict: true }` at all non-config boundaries: API responses, JSONL parsing, cache files, and subprocess output. Reserve lenient default (no options) for user-authored config files that may contain comments or trailing commas.

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-12T01:26:24.690Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/core/session-manager.ts:172-175
Timestamp: 2026-03-12T01:26:24.690Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument can be either a reviver function or an options object. To enable strict RFC 8259 validation (delegating to native `JSON.parse`), pass `{ strict: true }` as the second argument: `SafeJSON.parse(text, { strict: true })`. Use this form at all non-config boundaries (API responses, JSONL lines, cache files, subprocess output). The lenient default (comment-json) should only be used for user-authored config files that may contain comments or trailing commas.

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-12T01:26:33.308Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/mcp-tsc/utils/ServerManager.ts:0-0
Timestamp: 2026-03-12T01:26:33.308Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts exactly **two** arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument is a union of a reviver function or a ParseOptions object — there is no separate third `options` argument. To enable strict RFC 8259 validation (native JSON.parse), pass `{ strict: true }` as the second argument: `SafeJSON.parse(text, { strict: true })`. Use this form at all external API response boundaries, JSONL parsing points, cache files, machine-written JSON files, and subprocess output. The lenient default (comment-json) is reserved for user-authored config files that may contain comments or trailing commas.

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-12T01:26:09.054Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/automate/lib/step-runner.ts:68-72
Timestamp: 2026-03-12T01:26:09.054Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second parameter is either a reviver function or an options object (e.g., `{ strict: true }`). To enforce strict RFC 8259 validation at remote/third-party API response boundaries, subprocess stdout, JSONL parsing, and cache files, use `SafeJSON.parse(text, { strict: true })`. Do NOT pass a null placeholder before the options object — the 3-arg form does not exist. The `JSON.parse` call inside the strict branch must be suppressed with `// biome-ignore lint/style/noRestrictedGlobals: intentional strict-mode fallback to native JSON.parse`.

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-11T14:37:47.990Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 87
File: docs/typescript-sdks/apple/github-repos-index.md:94-94
Timestamp: 2026-03-11T14:37:47.990Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), docs under `docs/typescript-sdks/apple/` (e.g., `github-repos-index.md`, `macos-node-api.md`) are auto-generated. Do not flag trivial markdown formatting issues (e.g., MD022 blank lines around headings, MD031, etc.) in these files, as they will not be manually fixed.

Applied to files:

  • src/benchmark/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/say/index.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-15T16:03:33.870Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:59-67
Timestamp: 2026-03-15T16:03:33.870Z
Learning: In `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`, ubuntu-slim runner detection (1-core, $0.002/min) is intentionally omitted from `RUNNER_RATES` and `detectRunnerType`. The maintainer considers it rarely used in practice and has explicitly decided not to add detection for it. Do not flag its absence as an issue in future reviews.

Applied to files:

  • src/benchmark/index.ts
📚 Learning: 2026-03-17T01:47:59.765Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:209-222
Timestamp: 2026-03-17T01:47:59.765Z
Learning: In genesiscz/GenesisTools, files under .claude/plans/ are implementation plan documents, not production code. Do not raise code-quality, consistency, or implementation issues (e.g., naming conflicts, missing migrations, architectural concerns) against these plan files. Issues in plan documents are expected to be addressed during actual implementation. Only flag genuinely problematic structural issues (e.g., broken links, missing required sections) if they would block implementation. This extends the existing .claude/ directory exemption beyond markdownlint to all review categories.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
  • src/zsh/features/notify.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Support multiple output destinations (file, clipboard, stdout) and use `clipboardy` for clipboard operations

Applied to files:

  • src/say/index.ts
📚 Learning: 2026-03-12T01:26:41.292Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/utils/clarity/api.ts:0-0
Timestamp: 2026-03-12T01:26:41.292Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument can be either a reviver function or an options object (e.g. `{ strict: true }`). Use `SafeJSON.parse(text, { strict: true })` at remote/third-party API response boundaries (e.g. Clarity API, Timely API, Azure DevOps API) and JSONL/subprocess-output parsing points so malformed or non-standard responses fail fast. Fall back to the lenient default (no second arg, or `null`) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/say/index.ts
🧬 Code graph analysis (5)
src/notify/index.ts (2)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/macos/notifications.ts (1)
  • sendNotification (185-212)
src/utils/ai/providers/index.ts (4)
src/utils/ai/types.ts (3)
  • AIProviderType (1-1)
  • AIProvider (4-9)
  • AITask (2-2)
src/utils/ai/providers/AICloudProvider.ts (1)
  • AICloudProvider (29-163)
src/utils/ai/providers/AILocalProvider.ts (1)
  • AILocalProvider (22-149)
src/utils/ai/providers/AIDarwinKitProvider.ts (1)
  • AIDarwinKitProvider (5-60)
src/benchmark/index.ts (3)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/json.ts (1)
  • SafeJSON (24-58)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/ai/providers/AILocalProvider.ts (1)
src/utils/ai/types.ts (8)
  • AITask (2-2)
  • AITranscriptionProvider (46-48)
  • AITranslationProvider (50-52)
  • AISummarizationProvider (54-56)
  • TranscribeOptions (11-15)
  • TranslateOptions (24-28)
  • TranslationResult (30-34)
  • SummarizationResult (41-44)
src/say/index.ts (2)
src/utils/macos/tts.ts (5)
  • setMute (130-140)
  • speak (152-227)
  • getConfigForRead (114-116)
  • SayConfig (33-38)
  • setConfig (110-112)
src/utils/table.ts (1)
  • formatTable (13-47)
🔇 Additional comments (15)
src/notify/index.ts (1)

1-165: LGTM!

The notify CLI implementation is well-structured:

  • Properly uses commander for CLI parsing with negatable options (--no-ignore-dnd, --no-say)
  • Uses @clack/prompts for interactive configuration
  • Correctly uses program.outputHelp() + process.exit(0) pattern
  • Error handling via parseAsync() in a try/catch wrapper
src/say/index.ts (1)

1-276: LGTM!

The say CLI implementation is comprehensive and well-organized:

  • Correctly uses parseAsync() with try/catch error handling
  • Uses listVoicesStructured() to show all available voices in the picker
  • Interactive mode properly handles cancellation via p.isCancel()
  • Mute/unmute and config management follows established patterns
src/zsh/features/notify.ts (1)

3-115: LGTM!

The notify zsh feature implementation correctly addresses all previously identified issues:

  • Guard at lines 15-17 prevents DEBUG trap from clobbering tracked command when internal hooks fire
  • TERM_PROGRAM normalization (lines 63-70) ensures reliable frontmost-app comparison
  • Proper Bash integration with DEBUG trap guard and PROMPT_COMMAND handling for various Bash versions
  • Background notification dispatch via tools notify ... & disown is appropriate for non-blocking UX
src/benchmark/index.ts (9)

1-50: LGTM!

Clean imports following project conventions (picocolors over chalk, commander + @clack/prompts for CLI). Type definitions correctly model hyperfine's JSON output format.


56-77: LGTM!

Good use of Storage for config persistence, and the notify suite now correctly uses plain terminal-notifier for PATH resolution across different Homebrew installations.


83-118: LGTM!

Proper use of Bun.spawn() for hyperfine detection with clear error messaging. Suite management functions cleanly separate built-in from custom suites with Storage-backed persistence.


135-149: LGTM!

Clean implementation of result file management. The lexicographic sort on ISO-formatted dates correctly yields chronological ordering for comparison lookups.


155-198: LGTM!

Proper integration with hyperfine: correct arg building with --command-name for labeled output, Bun.spawn with inherited streams for live progress display, and Bun.write for result persistence. The file format transformation from hyperfine's output to SavedResult is a clean approach.


204-249: LGTM!

Clean display logic with proper unit conversion (seconds → milliseconds for formatDuration), intuitive color semantics for deltas (green = faster, red = slower), and graceful handling of missing previous results.


279-317: LGTM!

Solid input validation in cmdAdd: the first-colon parsing correctly handles commands containing colons, the 2-command minimum ensures meaningful benchmarks, and built-in suite protection prevents accidental overwrites.


361-440: LGTM!

Interactive mode makes good use of withCancel for graceful cancellation handling. The dynamic action options (delete only available for custom suites) provide appropriate UX constraints.


446-493: LGTM!

Clean CLI structure with Commander: default action intelligently switches between interactive and direct modes, subcommands are well-documented, and parseAsync with try/catch provides proper error handling.

src/zsh/features/dotdotdot.ts (2)

8-27: Good cursor-delta handling in expansion path.

Tracking old_len and applying a delta update keeps the cursor stable during expansion without forcing end-of-line.


64-75: Re-source guard and dynamic Tab widget capture look correct.

The binding checks prevent self-rebinding loops and preserve the currently bound Tab widget before wrapping.

src/utils/ai/providers/AILocalProvider.ts (1)

42-45: ⚠️ Potential issue | 🟠 Major

Fix the ASR timestamp option name.

Line 43 has a typo: return_ttimestamps should be return_timestamps. The correct API option (confirmed in @huggingface/transformers v3.8.1 docs) is return_timestamps. With this typo, timestamp chunks are not requested and the segments field will remain empty.

Diff
-            return_ttimestamps: true,
+            return_timestamps: true,
			> Likely an incorrect or invalid review comment.

genesiscz added a commit that referenced this pull request Mar 17, 2026
- t34/t35: isNaN → Number.isNaN in timer
- t36/t42: fix ms overflow in formatTimestamp (compute from totalMs)
- t38: use parseAsync() in transcribe CLI
- t33: update voice-memos usage docs
- t44: require --from for local translation instead of defaulting to English
- t45: memoize in-flight pipeline loads to prevent duplicate downloads
- t46: use LBUFFER/RBUFFER in dotdotdot collapse to preserve cursor position
- t31: clean up temp file after interactive summarize
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/zsh/features/dotdotdot.ts (1)

8-26: ⚠️ Potential issue | 🟡 Minor

Cursor adjustment may be incorrect when dots exist after cursor position.

The CURSOR += ${#BUFFER} - old_len adjustment (line 25) assumes all expanded dot sequences are at or before the cursor. When dots exist after the cursor, this shifts the cursor too far right.

Example: cd <cursor>.../foo — expansion adds 2 chars, but cursor should stay at position 3, not become 5.

In practice, this is an edge case since users typically type at end-of-line. The collapse_buf correctly preserves cursor via LBUFFER/RBUFFER, so the final position after Tab completion is correct. However, the intermediate position passed to the completion widget could be wrong.

🔧 Potential fix using LBUFFER/RBUFFER
 __genesis_dotdotdot_expand_buf() {
-    local old_len=\${`#BUFFER`}
-    local buf="$BUFFER"
-    # Process from longest (10 dots) down to 3 to avoid partial matches
-    local i ndots chain j
+    local left="$LBUFFER"
+    local right="$RBUFFER"
+    local i ndots chain j
     for (( i=10; i>=3; i-- )); do
         ndots=""
         for (( j=0; j<i; j++ )); do
             ndots="\${ndots}."
         done
         chain=".."
         for (( j=2; j<i; j++ )); do
             chain="../\${chain}"
         done
-        buf="\${buf//\${ndots}/\${chain}}"
+        left="\${left//\${ndots}/\${chain}}"
+        right="\${right//\${ndots}/\${chain}}"
     done
-    BUFFER="$buf"
-    (( CURSOR += \${`#BUFFER`} - old_len ))
+    LBUFFER="$left"
+    RBUFFER="$right"
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/zsh/features/dotdotdot.ts` around lines 8 - 26, The function
__genesis_dotdotdot_expand_buf updates BUFFER and then adjusts CURSOR using the
change in total BUFFER length, which incorrectly moves the cursor when dot
sequences after the cursor are expanded; instead, perform the dot-sequence
expansion only on the left side of the cursor (LBUFFER) or compute the delta
using LBUFFER lengths so the cursor only moves by expansions that occur before
it. Concretely: capture old_left_len=${`#LBUFFER`}, perform the same dot-to-chain
replacements on LBUFFER (or build new_left by applying the loop to LBUFFER),
rebuild BUFFER as new_left + RBUFFER, then set CURSOR += (${`#new_left`} -
old_left_len) (or set CURSOR=${`#new_left`}); reference
__genesis_dotdotdot_expand_buf, BUFFER, LBUFFER, RBUFFER, CURSOR and mimic the
collapse_buf approach to preserve correct cursor position.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ai/index.ts`:
- Around line 373-377: The log call that prints the HF token currently builds
the string with + concatenation (see hfToken, p.log.info and pc.dim usage);
change it to use a single template literal for the inner pc.dim argument so
interpolation handles slicing (e.g.,
``pc.dim(`${hfToken.slice(0,8)}...${hfToken.slice(-4)}`)``) and likewise for the
"not set" branch use a template literal for consistency.

In `@src/timer/index.ts`:
- Line 467: The CLI option "--repeat <n>" is not validated in
non-interactive/direct mode, allowing 0, negative, or NaN values to create
invalid/no-op timer loops; update the parsing/validation where the option is
consumed (the code that defines .option("--repeat <n>", ...) and the direct CLI
execution path that handles repeat instead of the interactive validator) to
coerce to an integer, check Number.isInteger and that the value is >= 1, and
reject or fallback to a sensible default (or print an error and exit) when
invalid; apply the same validation logic for the other repeat-related handling
mentioned around the same block (the code referenced at the second region around
lines 493-499) so both interactive and non-interactive flows enforce the same
rules.
- Around line 90-95: The function runForegroundTimer (and the other multi-arg
timer APIs flagged at lines 137-143 and 173-179) use five positional parameters
which violates the 3+ params rule; refactor these functions to accept a single
options object (e.g., { durationMs, title, notify, say, repeat }) instead of
positional args, update their callers to pass an object, and adjust any related
type/interface definitions and JSDoc to preserve defaults and optionality while
keeping the original parameter names as object keys for clarity.
- Around line 261-264: The write that persists changed timers should be
performed under the same configuration lock as other mutators to avoid race
conditions: instead of directly assigning data.timers = alive and calling
saveActiveTimers(data) in listTimers, wrap that assignment and the
saveActiveTimers call in withConfigLock so the update is atomic with other
config mutations; update the listTimers code to use withConfigLock around the
data.timers update and save to prevent concurrent timer processes from dropping
updates.

In `@src/utils/ai/providers/AILocalProvider.ts`:
- Around line 141-164: The decodeAudio function assumes a fixed 44-byte WAV
header which fails for files with extra chunks; update decodeAudio to parse RIFF
chunks: after verifying "RIFF" and "WAVE", iterate chunks starting at offset 12,
reading each 4-byte chunk ID and 4-byte chunk size (ensure bounds checking and
pad odd sizes) to locate the "fmt " chunk (read numChannels and bitsPerSample
from its payload instead of fixed offsets) and the "data" chunk (set dataOffset
to the start of its payload and use its size), then use pcmData =
audio.subarray(dataOffset, dataOffset + dataSize) and proceed with 16-bit
decoding; keep references to decodeAudio, numChannels, bitsPerSample,
dataOffset, and pcmData when implementing.

In `@src/youtube/commands/transcribe.ts`:
- Line 242: The log call in transcribe.ts uses string concatenation; update the
p.log.error invocation to use a template literal so the message and
pc.cyan("brew install yt-dlp") are combined with `${...}`. Locate
p.log.error(...) in the transcribe command (function/method handling yt-dlp
checks) and replace the concatenated string with a single template literal
including the pc.cyan call.

---

Outside diff comments:
In `@src/zsh/features/dotdotdot.ts`:
- Around line 8-26: The function __genesis_dotdotdot_expand_buf updates BUFFER
and then adjusts CURSOR using the change in total BUFFER length, which
incorrectly moves the cursor when dot sequences after the cursor are expanded;
instead, perform the dot-sequence expansion only on the left side of the cursor
(LBUFFER) or compute the delta using LBUFFER lengths so the cursor only moves by
expansions that occur before it. Concretely: capture old_left_len=${`#LBUFFER`},
perform the same dot-to-chain replacements on LBUFFER (or build new_left by
applying the loop to LBUFFER), rebuild BUFFER as new_left + RBUFFER, then set
CURSOR += (${`#new_left`} - old_left_len) (or set CURSOR=${`#new_left`}); reference
__genesis_dotdotdot_expand_buf, BUFFER, LBUFFER, RBUFFER, CURSOR and mimic the
collapse_buf approach to preserve correct cursor position.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 24b7d5f3-e4d2-4c81-97aa-4058a8d39719

📥 Commits

Reviewing files that changed from the base of the PR and between 43a053c and 262997a.

📒 Files selected for processing (7)
  • src/ai/index.ts
  • src/macos/index.ts
  • src/timer/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/youtube/commands/transcribe.ts
  • src/zsh/features/dotdotdot.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Do not add file-path comments as the first line of files
Do not add obvious comments that restate what the code already says
Never use one-line if statements; always use block form with braces
Include an empty line before if statements, unless the preceding line is a variable declaration used by that if
Include an empty line after closing } unless followed by else, catch, finally, or another }
Use object parameters for functions with 3+ parameters or optional parameters
Use positional parameters only for 1-2 required and obvious parameters such as estimateTokens(text) or resolve(base, path)
Prefer error: err over error: err instanceof Error ? err.message : String(err) when the error field accepts unknown

Files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Do not use as any for type casting; use proper type narrowing, type guards, or explicit interfaces instead
Use discriminant checks for union types (e.g., entity.className === "User")

Files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
src/**/index.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/index.{ts,tsx}: Use commander for parsing command-line arguments with subcommands and options
Use @clack/prompts for interactive user experience in new tools; use @inquirer/prompts only for legacy tools
Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations
Use chalk for colored terminal output but strip ANSI codes for non-TTY environments
Use Bun.spawn() for executing external commands and handle stdout/stderr streams properly
Use Node.js path module for cross-platform path handling and resolve relative paths to absolute
Use Bun's native file APIs (Bun.write()) for better performance in file operations

Files:

  • src/timer/index.ts
  • src/macos/index.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
src/utils/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place general-purpose utility functions in src/utils/ instead of inside tool directories

Files:

  • src/utils/ai/providers/AILocalProvider.ts
🧠 Learnings (26)
📓 Common learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-16-ToolsEnhancements.md:131-131
Timestamp: 2026-03-17T01:47:54.498Z
Learning: In the genesiscz/GenesisTools repository, files under `.claude/plans/` (e.g., `.claude/plans/2026-03-16-ToolsEnhancements.md`) are implementation plan documents, not production code. Do not flag implementation-level issues (e.g., filename uniqueness, overwrite risks, edge cases) in these files, as such concerns are addressed in the actual implementation. Treat these documents as design artifacts only.
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-02-20T00:52:27.023Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 31
File: src/ask/utils/helpers.ts:3-3
Timestamp: 2026-02-20T00:52:27.023Z
Learning: In all TypeScript source files under src, prefer using picocolors for colored terminal output in new code. Picocolors is smaller and faster than chalk, so adopt it for CLI output coloring and avoid adding chalk in new code paths unless there is a compelling compatibility reason.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:30:56.939Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:130-139
Timestamp: 2026-03-17T01:30:56.939Z
Learning: In genesiscz/GenesisTools, do not suggest converting two-argument functions with an optional second parameter (for example setMute(muted: boolean, app?: string)) to an object-parameter form. The project prefers simple positional parameters for short utility functions, even when an optional argument is present. The object-parameter guideline should only apply when a function has 3 or more parameters.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
  • src/macos/index.ts
  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T16:03:57.231Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:262-303
Timestamp: 2026-03-15T16:03:57.231Z
Learning: In `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`, the `durations` array in `calculateRunCost()` and `WorkflowSummary` intentionally stores one entry per job (job-level duration in seconds), not per-run wall-clock time. This matches GitHub's billing model (rounded up per job). The aggregated/top-N output represents total billable job time, not wall-clock run time. Do not flag this as an issue in future reviews.

Applied to files:

  • src/youtube/commands/transcribe.ts
  • src/transcribe/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `commander` for parsing command-line arguments with subcommands and options

Applied to files:

  • src/macos/index.ts
  • src/transcribe/index.ts
📚 Learning: 2026-03-15T15:54:08.510Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 105
File: src/ask/index.ts:66-74
Timestamp: 2026-03-15T15:54:08.510Z
Learning: In genesiscz/GenesisTools, manual argv._[0] dispatch in src/ask/index.ts (and similar entry-point files) is intentional for lazy loading: subcommand modules (e.g. ask/commands/configure) are imported via dynamic import() inside the dispatch branch rather than registered as Commander subcommands. This avoids eagerly loading all subcommand modules at startup. Do NOT flag this pattern as a violation of the "use Commander for subcommands" guideline; the lazy-loading benefit outweighs the missing Commander help/error-handling for these entry points.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/ai/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-09T13:39:43.059Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:47:58.691Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:148-148
Timestamp: 2026-03-17T01:47:58.691Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation plan documents (design/intent descriptions), not production code. Do not raise code-quality, robustness, or implementation-detail review comments (e.g., race conditions, error handling, temp file strategy) on these plan files. Such concerns are addressed in the actual implementation files (e.g., src/**/*.ts).

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:32:56.919Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:122-127
Timestamp: 2026-03-17T01:32:56.919Z
Learning: In genesiscz/GenesisTools, mute/config state (SayConfig, isMuted, setMute, getConfig, getConfigForRead) belongs at the CLI layer (src/say/) rather than in the TTS utility layer (src/utils/macos/tts.ts). The tts.ts file should remain a pure macOS TTS primitive (getVoiceMap, speak, listVoices, listVoicesStructured) without any application-level config or mute logic. The `app` field in SpeakOptions should also be removed since it only exists to support per-app muting.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T17:12:27.285Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/clarity/ui/src/routes/settings.tsx:1-1
Timestamp: 2026-03-11T17:12:27.285Z
Learning: In genesiscz/GenesisTools, `SafeJSON` in `src/utils/json.ts` intentionally uses `comment-json.parse` for parsing (to handle `//` comments, trailing commas, unquoted keys) but `JSON.stringify` (native) for stringification. Do not flag `SafeJSON.stringify` usages as unnecessary — the intent is a unified drop-in for `JSON` that parses leniently but serialises as standard JSON.

Applied to files:

  • src/zsh/features/dotdotdot.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `clack/prompts` for interactive user experience in new tools; use `inquirer/prompts` only for legacy tools

Applied to files:

  • src/ai/index.ts
  • src/transcribe/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Support multiple output destinations (file, clipboard, stdout) and use `clipboardy` for clipboard operations

Applied to files:

  • src/transcribe/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `Bun.spawn()` for executing external commands and handle stdout/stderr streams properly

Applied to files:

  • src/transcribe/index.ts
📚 Learning: 2026-03-17T01:47:59.002Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T14:37:47.990Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 87
File: docs/typescript-sdks/apple/github-repos-index.md:94-94
Timestamp: 2026-03-11T14:37:47.990Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), docs under `docs/typescript-sdks/apple/` (e.g., `github-repos-index.md`, `macos-node-api.md`) are auto-generated. Do not flag trivial markdown formatting issues (e.g., MD022 blank lines around headings, MD031, etc.) in these files, as they will not be manually fixed.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:48:08.317Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:209-222
Timestamp: 2026-03-17T01:48:08.317Z
Learning: In genesiscz/GenesisTools, files under .claude/plans/ are implementation plan documents, not production code. Do not raise code-quality, consistency, or implementation issues (e.g., naming conflicts, missing migrations, architectural concerns) against these plan files. Issues in plan documents are expected to be addressed during actual implementation. Only flag genuinely problematic structural issues (e.g., broken links, missing required sections) if they would block implementation. This extends the existing .claude/ directory exemption beyond markdownlint to all review categories.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
🧬 Code graph analysis (4)
src/timer/index.ts (3)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/format.ts (1)
  • parseDuration (123-159)
src/youtube/commands/transcribe.ts (3)
src/utils/json.ts (1)
  • SafeJSON (24-58)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • spinner (50-52)
src/ask/audio/TranscriptionManager.ts (1)
  • transcriptionManager (401-401)
src/macos/index.ts (1)
src/macos/commands/voice-memos/index.ts (1)
  • registerVoiceMemosCommand (17-65)
src/ai/index.ts (5)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/ai/index.ts (1)
  • AI (13-44)
src/utils/clipboard.ts (2)
  • copyToClipboard (4-14)
  • readFromClipboard (16-18)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/ai/types.ts (1)
  • AITask (2-2)
🪛 GitHub Actions: CI
src/youtube/commands/transcribe.ts

[error] 242-242: lint/style/useTemplate FIXABLE: Template literals are preferred over string concatenation. Unsafe fix suggested: convert to template literal.

src/ai/index.ts

[error] 374-374: lint/style/useTemplate FIXABLE: Template literals are preferred over string concatenation. Unsafe fix suggested: use a template literal instead of string concatenation in HF Token log.

🔇 Additional comments (32)
src/macos/index.ts (3)

13-18: Voice Memos usage docs are now correctly aligned with the command surface.

This matches the registered subcommands (list, play, export, transcribe, search) and avoids misleading help text.


27-27: Import addition is correct and used.

registerVoiceMemosCommand is properly imported and consumed by program setup.


40-40: Voice Memos command registration is wired correctly.

Good integration into the existing macos command bootstrap flow.

src/utils/ai/providers/AILocalProvider.ts (5)

1-33: LGTM on imports, type definitions, and class setup.

The memoization fix with pendingPipelines addresses the previous concern about duplicate model loads. The interface implementations and supported tasks are well-defined.


35-59: LGTM on transcribe implementation.

The audio decoding step before pipeline execution correctly addresses the previous issue of passing raw encoded buffers to the Whisper model.


61-92: LGTM on translate implementation.

The fix requiring --from unless an explicit --model is provided, along with the try/catch with helpful error messaging for unsupported language pairs, addresses previous concerns.


108-139: LGTM on getPipeline memoization.

The pendingPipelines map correctly prevents duplicate concurrent downloads by caching the in-flight Promise and cleaning up in the finally block.


166-172: LGTM on dispose implementation.

Properly iterates through all cached pipelines and clears the map. Swallowing errors during cleanup is acceptable.

src/ai/index.ts (6)

1-18: LGTM on imports and module setup.

Correctly uses @clack/prompts for interactive experience, commander for CLI parsing, and picocolors for colored output per project conventions. Node.js path module used for cross-platform handling.


50-109: LGTM on translate command implementation.

Clean flow with stdin fallback, Darwin-specific language detection, proper spinner UX, and correct translator disposal in the finally block.


115-173: opts.provider is still ignored in summarize command.

Line 156 calls AI.summarize(input, { maxLength }) without forwarding opts.provider. The --provider option advertised at line 620 has no effect.

🔧 Proposed fix
-        const result = await AI.summarize(input, { maxLength });
+        const summarizer = await AI.Summarizer.create({ provider: opts.provider });
+        try {
+            const result = await summarizer.summarize(input, { maxLength });

And add matching finally { summarizer.dispose(); } pattern.


240-284: opts.provider is still ignored in classify command.

Line 268 always calls classifyText() from the macOS utility layer regardless of opts.provider. The --provider option at line 641 is ineffective.


531-544: LGTM on temp file cleanup.

The interactive summarize flow now correctly cleans up the temp file using try/finally with unlink().catch(), addressing the previous concern about leaving user data on disk.


594-687: LGTM on CLI structure.

Commander setup is well-organized with clear descriptions, proper option definitions, and consistent action handlers. The main() wrapper provides top-level error handling.

src/zsh/features/dotdotdot.ts (4)

28-46: LGTM — correctly preserves cursor position.

Using LBUFFER and RBUFFER instead of operating on BUFFER with a forced cursor reset properly maintains the cursor position during collapse. This addresses the previous review concern about mid-buffer completion.


48-64: LGTM — clean expand/collapse orchestration.

The did_expand flag correctly gates the collapse operation, and the overall flow ensures Tab completion operates on expanded paths while preserving the user's dot notation in the final buffer.


66-70: LGTM — safe re-source handling.

The pattern check prevents circular widget references when .zshrc is sourced multiple times.


72-77: LGTM — dynamic Tab widget hooking with proper safeguards.

The typeset usage at top-level is correct (addresses the previous local issue), and the dynamic detection of the current ^I binding allows this to work with various completion systems. The -n check handles cases where Tab is unbound.

src/transcribe/index.ts (7)

1-28: LGTM!

Clean imports and well-structured constants. The SUPPORTED_AUDIO_EXTENSIONS Set provides O(1) lookup, and using picocolors aligns with the repository preference for lightweight colored output.


34-50: LGTM!

The millisecond overflow issue is properly addressed—deriving all components from totalMs ensures ms is always within the valid 0–999 range.


52-80: Untimed text still emits invalid subtitle formats.

When result.segments is missing or empty, toSRT() returns raw text and toVTT() returns WEBVTT plus raw text—neither are valid timed subtitle files. This matches the concern raised previously.


82-107: LGTM!

The formatOutput switch exhaustively handles all OutputFormat cases, and the TranscribeFlags interface properly groups options as an object parameter.


109-207: LGTM!

Solid implementation with proper error handling, spinner UX, and resource cleanup via transcriber.dispose(). Metadata correctly goes to stderr, keeping stdout clean for piping. The output routing logic handles all destination combinations appropriately.


213-315: LGTM!

Interactive mode properly uses @clack/prompts with consistent cancellation handling after each user input. The default output path derivation (line 291) is a nice UX touch.


341-351: LGTM!

The parseAsync() call is correctly used for the async action handler, and wrapping it in main() with try/catch provides clean error handling at the top level.

src/timer/index.ts (1)

206-210: ⚠️ Potential issue | 🟠 Major

Background repeat timers expose stale progress/remaining data.

Line 206 stores endTime for one cycle only, and Lines 228-231 never persist cycle progress. list then shows inaccurate Remaining/Cycle while repeat timers are still running.

Proposed fix
-    const entry: TimerEntry = {
+    const totalMs = durationMs * repeat;
+    const entry: TimerEntry = {
         id,
         pid,
         title,
         durationMs,
-        endTime: Date.now() + durationMs,
+        endTime: Date.now() + totalMs,
         notify,
         say,
         repeat,
         currentCycle: 1,
     };
 async function handleBackgroundRun(args: string[]): Promise<void> {
@@
     for (let cycle = 1; cycle <= repeat; cycle++) {
+        await storage.withConfigLock(async () => {
+            const data = await getActiveTimers();
+            const timer = data.timers.find((t) => t.id === id);
+            if (timer) {
+                timer.currentCycle = cycle;
+                timer.endTime = Date.now() + durationMs * (repeat - cycle + 1);
+                await saveActiveTimers(data);
+            }
+        });
+
         await new Promise<void>((resolve) => setTimeout(resolve, durationMs));
         await fireCompletionActions(title, notify, say, cycle, repeat);
     }

Also applies to: 228-231, 272-286

⛔ Skipped due to learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:262-303
Timestamp: 2026-03-15T16:03:57.231Z
Learning: In `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`, the `durations` array in `calculateRunCost()` and `WorkflowSummary` intentionally stores one entry per job (job-level duration in seconds), not per-run wall-clock time. This matches GitHub's billing model (rounded up per job). The aggregated/top-N output represents total billable job time, not wall-clock run time. Do not flag this as an issue in future reviews.
src/youtube/commands/transcribe.ts (6)

34-51: LGTM!

The video ID extraction handles all common YouTube URL patterns including watch URLs, short URLs, embeds, and shorts.


90-123: LGTM!

Audio download implementation properly configures yt-dlp for Whisper-compatible format (16kHz mono WAV), handles errors, and verifies the output file exists.


125-133: LGTM!

The timestamp formatting now correctly computes all fields from total milliseconds via integer division/modulo, ensuring ms is always in the valid 0–999 range. This addresses the previous overflow concern.


266-270: Audio fallback discards segment timing data.

The segments: [] hardcoding loses any timing information that transcriptionManager.transcribeAudio() may return. With --format srt or --format vtt, this produces empty or header-only subtitle files despite having transcription text.


293-307: LGTM!

The command setup is clean with well-documented options and proper action handler delegation.


68-72: > Likely an incorrect or invalid review comment.

@genesiscz
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

♻️ Duplicate comments (10)
src/utils/macos/voice-memos.ts (2)

216-238: ⚠️ Potential issue | 🟡 Minor

Use strict JSON parsing for embedded tsrp payload.

The parseTsrpPayload function parses JSON from a file-format boundary (embedded in MPEG-4 atoms), not user-authored config. Per project guidelines, use strict RFC 8259 validation.

🔧 Proposed fix
 function parseTsrpPayload(json: string): TranscriptionResult | null {
     try {
-        const data = SafeJSON.parse(json);
+        const data = SafeJSON.parse(json, { strict: true });

         if (!data || typeof data !== "object") {
             return null;
         }

Based on learnings: "Use SafeJSON.parse(text, { strict: true }) at non-config JSON boundaries."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/voice-memos.ts` around lines 216 - 238, Update
parseTsrpPayload to use strict RFC‑8259 JSON parsing by passing the strict
option into SafeJSON.parse; specifically, replace the call to
SafeJSON.parse(json) inside parseTsrpPayload with SafeJSON.parse(json, { strict:
true }) so the function still validates the same attributedString/string + runs
patterns but rejects non‑RFC8259 input.

123-147: ⚠️ Potential issue | 🟠 Major

Performance: hasTranscript reads entire audio file for each memo during listing.

The rowToMemo function calls hasTranscript(filePath) which reads the complete audio file into memory. Since listMemos() maps every row through rowToMemo(), this scales with total audio bytes rather than memo count. Consider deferring transcript detection until explicitly requested or implementing a lightweight metadata check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/voice-memos.ts` around lines 123 - 147, rowToMemo currently
calls hasTranscript(filePath) for every memo which forces reading entire audio
files during listMemos; remove that eager call and instead either (A) set
hasTranscript to false/undefined in rowToMemo and implement a separate
memoHasTranscript(filePath) function that performs a lightweight check (e.g.,
check for companion transcript files or metadata rather than reading audio) to
be called on demand, or (B) add an optional parameter to
listMemos/includeTranscript flag so callers can opt into computing transcripts
and only then invoke hasTranscript; update usages of rowToMemo, listMemos, and
any consumers to use the new on-demand memoHasTranscript or the flag-based path.
src/timer/index.ts (3)

261-264: ⚠️ Potential issue | 🟠 Major

Protect cleanup persistence writes with withConfigLock.

Line 261 writes timer state without the config lock, while other mutators are lock-protected. Concurrent start/cancel/list flows can drop updates.

Proposed fix
     if (changed) {
-        data.timers = alive;
-        await saveActiveTimers(data);
+        await storage.withConfigLock(async () => {
+            const latest = await getActiveTimers();
+            latest.timers = latest.timers.filter((t) => isProcessAlive(t.pid));
+            await saveActiveTimers(latest);
+        });
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` around lines 261 - 264, The cleanup branch updates
in-memory timers and persists them without acquiring the config lock; wrap the
mutation and persistence in withConfigLock to avoid races. Replace the plain if
(changed) { data.timers = alive; await saveActiveTimers(data); } with a call to
withConfigLock that acquires the config lock and inside sets data.timers = alive
and calls saveActiveTimers(data) so both the in-memory assignment and the
persistence are atomic relative to other mutators (refer to withConfigLock and
saveActiveTimers in src/timer/index.ts).

493-499: ⚠️ Potential issue | 🟠 Major

Validate repeat in direct CLI mode before starting timers.

Interactive mode validates repeat, but direct mode does not. Invalid values (NaN, 0, negative, non-integer) can still reach execution paths.

Proposed fix
             const repeat = options.repeat ?? 1;
+
+            if (Number.isNaN(repeat) || repeat < 1 || !Number.isInteger(repeat)) {
+                console.log(pc.red(`Invalid repeat value: ${String(options.repeat)}`));
+                console.log(pc.dim("Repeat must be a positive integer."));
+                process.exit(1);
+            }
 
             if (options.bg) {
                 await startBackgroundTimer(durationMs, timerTitle, doNotify, doSay, repeat);
             } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` around lines 493 - 499, The direct CLI path is missing
validation for options.repeat before invoking startBackgroundTimer or
runForegroundTimer; ensure the code that sets repeat (currently const repeat =
options.repeat ?? 1) validates that options.repeat is a finite integer >= 1 (and
not NaN, 0, negative, or fractional) the same way the interactive mode does, and
either normalize/ clamp it to 1 or return an error/exit if invalid; apply this
check before calling startBackgroundTimer(...) and runForegroundTimer(...),
referencing options.repeat and the repeat argument passed into those functions
so invalid values never reach startBackgroundTimer or runForegroundTimer.

90-95: 🛠️ Refactor suggestion | 🟠 Major

Refactor multi-argument timer APIs to an options object.

These signatures use 5 positional arguments, which makes call sites brittle and violates the repo rule for 3+ parameters.

As per coding guidelines: "Use object parameters for functions with 3+ parameters or optional parameters".

Also applies to: 137-143, 173-179

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` around lines 90 - 95, The runForegroundTimer function
currently takes five positional args (durationMs, title, notify, say, repeat);
refactor it to accept a single options object (e.g., { durationMs, title,
notify, say, repeat }) and update the implementation to destructure those
properties, and update all other timer functions in this module with 3+ params
likewise (the other timer APIs in this file that mirror runForegroundTimer).
Also update every call site to pass the options object (or add a small
compatibility wrapper that accepts the old positional signature and converts to
the options object) so behavior remains unchanged while satisfying the 3+
parameter rule.
src/ai/index.ts (3)

267-268: ⚠️ Potential issue | 🟠 Major

classify --provider still never changes the execution path.

This dispatch is hard-wired to classifyText(), so opts.provider remains unused and the command cannot select any non-Darwin implementation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ai/index.ts` around lines 267 - 268, The classify command currently
always calls classifyText(input, categories) and ignores opts.provider; update
the dispatch so opts.provider selects the implementation (e.g., switch/lookup on
opts.provider) and call the provider-specific function instead of hard-wiring
classifyText; locate the call site where classifyText is invoked (the classify
command handler around the try { const result = await classifyText(input,
categories); }) and refactor to use a provider dispatch (for example a map from
provider name to handler or a switch that calls classifyText,
classifyTextDarwin, classifyTextOtherProvider, etc.), passing through the same
arguments and preserving error handling and return shape.

155-156: ⚠️ Potential issue | 🟠 Major

summarize --provider is still a dead flag.

Line 156 calls AI.summarize(input, { maxLength }), so opts.provider never reaches AI.Summarizer.create(). This command still always runs with the configured/default provider despite advertising the override.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ai/index.ts` around lines 155 - 156, The summarize command never forwards
the CLI override to the AI layer—call sites use AI.summarize(input, { maxLength
}) so opts.provider is dropped; update the handler so the provider flag is
forwarded (e.g., include provider: opts.provider) into the options passed to
AI.summarize, and ensure AI.summarize (and/or AI.Summarizer.create) accepts and
uses that provider option to instantiate the chosen provider instead of the
configured default; reference AI.summarize, AI.Summarizer.create, and
opts.provider when making the change.

373-376: ⚠️ Potential issue | 🟡 Minor

Use the template literal here to clear the failing CI rule.

The masked token still uses string concatenation inside pc.dim(...), which is the lint failure reported by CI.

🔧 Minimal fix
-        p.log.info(`HF Token: ${pc.dim(hfToken.slice(0, 8) + "..." + hfToken.slice(-4))}`);
+        p.log.info(`HF Token: ${pc.dim(`${hfToken.slice(0, 8)}...${hfToken.slice(-4)}`)}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ai/index.ts` around lines 373 - 376, Replace the string concatenation
inside the pc.dim call with a template literal to satisfy the lint rule: update
the logging branch that checks hfToken (the p.log.info call using pc.dim(...))
to construct the masked token with a template literal (using ${...}) rather than
using "+" concatenation, keeping the same slices (hfToken.slice(0, 8) and
hfToken.slice(-4)) and message structure.
src/transcribe/index.ts (1)

55-72: ⚠️ Potential issue | 🟠 Major

Don't emit untimed text as srt/vtt.

When result.segments is missing, these helpers still return plain text, so --format srt/vtt can silently produce invalid subtitle files. Either synthesize a single cue from result.duration or fail fast.

🧩 One safe fallback
 function toSRT(result: TranscriptionResult): string {
     if (!result.segments?.length) {
-        return result.text;
+        if (!result.duration) {
+            throw new Error("SRT output requires timestamped segments or a known duration");
+        }
+
+        return `1\n00:00:00,000 --> ${formatTimestamp(result.duration, ",")}\n${result.text.trim()}`;
     }
 
     return result.segments
@@
 function toVTT(result: TranscriptionResult): string {
     if (!result.segments?.length) {
-        return `WEBVTT\n\n${result.text}`;
+        if (!result.duration) {
+            throw new Error("VTT output requires timestamped segments or a known duration");
+        }
+
+        return `WEBVTT\n\n00:00:00.000 --> ${formatTimestamp(result.duration, ".")}\n${result.text.trim()}`;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/transcribe/index.ts` around lines 55 - 72, The helpers toSRT and toVTT
currently emit plain untimed text when result.segments is missing; update them
to either synthesize a single timed cue using result.duration or fail fast: if
no segments and result.duration is present, create one cue from 0 to
result.duration (use formatTimestamp for the end time, "0" or formatTimestamp(0)
for the start), for SRT produce a numbered cue "1\nstart --> end\n{text.trim()}"
and for VTT include the "WEBVTT" header plus the single cue, otherwise
throw/return an error indicating missing timestamps so callers requesting
"srt"/"vtt" don't get invalid subtitle files; reference functions toSRT and
toVTT and the TranscriptionResult.{segments,duration} fields when making the
change.
src/youtube/commands/transcribe.ts (1)

266-272: ⚠️ Potential issue | 🟠 Major

Preserve timing data on the audio fallback path.

This still hardcodes segments: []. --format srt becomes empty and --format vtt becomes header-only even when transcription.text exists. Reuse the timing data returned by transcribeAudio() when available, or reject subtitle formats on this path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/youtube/commands/transcribe.ts` around lines 266 - 272, The fallback
branch builds a CaptionResult with segments: [] which loses timing data and
breaks subtitle outputs; update the fallback in transcribe.ts to reuse the
timing/segments returned by transcribeAudio() (or any variable holding its
result) when available instead of forcing segments: [], so that CaptionResult
(used by formatOutput) contains real segment timings, or alternatively
throw/reject when format is a subtitle type (srt/vtt) if no timing data exists;
locate the creation of the result object (CaptionResult) and the call site where
transcription/text is set and patch it to copy the segments from the
transcribeAudio() response before calling formatOutput.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/macos/commands/voice-memos/index.ts`:
- Around line 255-292: transcribeAll currently only extracts embedded
transcripts and never performs AI fallback like transcribeOne; update it to
either (A) be renamed to extractAllTranscripts and adjust the final info
message, or (B) implement AI fallback by converting transcribeAll to async and
for each memo where extractTranscript(memo.path) returns null call the same AI
transcription logic used in transcribeOne (or simply await transcribeOne(memo)
if that helper exists), then increment counters accordingly and await async
results before logging; make sure to update the function signature from function
transcribeAll(force: boolean): void to async function transcribeAll(force:
boolean): Promise<void> when choosing the AI fallback path.
- Line 31: Replace the unsafe parseInt usages by explicitly specifying the radix
10: update the .argument("<id>", "Memo ID", parseInt) call and the other
parseInt callbacks in this module to use an arrow wrapper like (v) =>
parseInt(v, 10) (or Number(v) if preferred) so the CLI argument parsers pass a
base-10 integer; specifically change the parseInt references used as
argument/option parsers to (v) => parseInt(v, 10).

In `@src/timer/index.ts`:
- Line 514: Remove the redundant inline comment "// Handle internal background
runner" that simply restates the following conditional/branch behavior; locate
the comment in the timer module (the comment immediately preceding the internal
background runner branch) and delete it, leaving the code and any meaningful
explanatory comments intact and avoiding other obvious restatements elsewhere in
the same function.
- Around line 228-231: The repeat loop advances cycles but never persists
progress, leaving stored currentCycle and endTime stale; after each cycle
(inside the for loop, immediately after await fireCompletionActions(...)) fetch
the timer record and update its currentCycle to the new cycle value and
recompute/persist endTime (e.g., now + remaining duration or original endTime +
durationMs) using the module's timer storage API so `timer list` reflects
updated cycle and remaining time; ensure you use the same timer identifier used
to start the timer and call the existing persistence/update function rather than
creating a new timer.
- Around line 474-480: The duration parsing block using parseDuration (variables
duration and durationMs) currently only checks for <= 0; add an upper-bound
validation to reject values greater than the JS setTimeout limit (2,147,483,647
ms) before proceeding — log an error via console (matching the existing style
with pc.red/pc.dim) and process.exit(1) if durationMs exceeds this limit; apply
the same upper-bound check in the other occurrence that uses parseDuration (the
earlier branch around line 229) so both callers guard against setTimeout
overflow.

In `@src/utils/ai/providers/AILocalProvider.ts`:
- Around line 202-211: The cache cleanup builds an incorrect modelCacheDir
because model.replace("/", "/") is a no-op; change the construction to match the
project's cache naming convention (models--<model-with-slashes-replaced-by-->)
by replacing all "/" with "--" when building modelCacheDir (use the same pattern
as ModelManager.ts: models--${modelId.replace(/\//g, "--")}); update the code
around cacheDir/model/modelCacheDir (and keep using rmSync with recursive/force)
so cleanup targets the real HuggingFace cache directory.

In `@src/utils/audio/converter.test.ts`:
- Around line 91-102: Update the test "convertToWhisperWav converts synthetic
MP3-like buffer" to assert that convertToWhisperWav rejects for invalid input
instead of allowing success: call await
expect(convertToWhisperWav(garbage)).rejects.toThrow(/Cannot convert audio/) (or
equivalent assertion) so the test fails if the function unexpectedly accepts the
garbage buffer, and remove the try/catch that masks successful resolution.

In `@src/utils/audio/converter.ts`:
- Around line 47-65: convertFileToWhisperWav currently only lets
tryAfconvert/tryFfmpeg delete outputPath on success, which leaks partial WAVs on
failure; modify convertFileToWhisperWav so outputPath is always removed on all
exit paths: wrap the conversion attempts in a try/finally (or catch+rethrow) and
in the finally check for and unlink the temporary outputPath if the conversion
did not succeed, while preserving successful return values; reference functions
convertFileToWhisperWav, tryAfconvert, tryFfmpeg and the caller
convertToWhisperWav (which already cleans tmpIn) to ensure tmpIn remains handled
and outputPath is cleaned on errors.

In `@src/utils/terminal.ts`:
- Around line 1-8: Trim redundant comments for detectTerminalApp: remove the
JSDoc block and the inline note that merely restate the implementation (e.g.,
"Checks CMUX_BUNDLE_ID first... then falls back to TERM_PROGRAM" and "cmux uses
libghostty...") and keep only a minimal comment containing any non-obvious
rationale if necessary; update the comment immediately above the
detectTerminalApp function to be concise and focused on rationale rather than
restating behavior.

In `@src/youtube/commands/transcribe.ts`:
- Around line 280-289: The catch block for the transcription flow currently
calls process.exit(1) which aborts the process before the finally cleanup runs;
in the catch in transcribe (the block that calls spinner.stop, p.log.error and
logger.error) replace the synchronous exit with setting process.exitCode = 1 and
then return from the function so the finally block (which unlinks audioPath)
executes; retain the existing logging (p.log.error and logger.error) and
spinner.stop calls so errors are logged before returning.

---

Duplicate comments:
In `@src/ai/index.ts`:
- Around line 267-268: The classify command currently always calls
classifyText(input, categories) and ignores opts.provider; update the dispatch
so opts.provider selects the implementation (e.g., switch/lookup on
opts.provider) and call the provider-specific function instead of hard-wiring
classifyText; locate the call site where classifyText is invoked (the classify
command handler around the try { const result = await classifyText(input,
categories); }) and refactor to use a provider dispatch (for example a map from
provider name to handler or a switch that calls classifyText,
classifyTextDarwin, classifyTextOtherProvider, etc.), passing through the same
arguments and preserving error handling and return shape.
- Around line 155-156: The summarize command never forwards the CLI override to
the AI layer—call sites use AI.summarize(input, { maxLength }) so opts.provider
is dropped; update the handler so the provider flag is forwarded (e.g., include
provider: opts.provider) into the options passed to AI.summarize, and ensure
AI.summarize (and/or AI.Summarizer.create) accepts and uses that provider option
to instantiate the chosen provider instead of the configured default; reference
AI.summarize, AI.Summarizer.create, and opts.provider when making the change.
- Around line 373-376: Replace the string concatenation inside the pc.dim call
with a template literal to satisfy the lint rule: update the logging branch that
checks hfToken (the p.log.info call using pc.dim(...)) to construct the masked
token with a template literal (using ${...}) rather than using "+"
concatenation, keeping the same slices (hfToken.slice(0, 8) and
hfToken.slice(-4)) and message structure.

In `@src/timer/index.ts`:
- Around line 261-264: The cleanup branch updates in-memory timers and persists
them without acquiring the config lock; wrap the mutation and persistence in
withConfigLock to avoid races. Replace the plain if (changed) { data.timers =
alive; await saveActiveTimers(data); } with a call to withConfigLock that
acquires the config lock and inside sets data.timers = alive and calls
saveActiveTimers(data) so both the in-memory assignment and the persistence are
atomic relative to other mutators (refer to withConfigLock and saveActiveTimers
in src/timer/index.ts).
- Around line 493-499: The direct CLI path is missing validation for
options.repeat before invoking startBackgroundTimer or runForegroundTimer;
ensure the code that sets repeat (currently const repeat = options.repeat ?? 1)
validates that options.repeat is a finite integer >= 1 (and not NaN, 0,
negative, or fractional) the same way the interactive mode does, and either
normalize/ clamp it to 1 or return an error/exit if invalid; apply this check
before calling startBackgroundTimer(...) and runForegroundTimer(...),
referencing options.repeat and the repeat argument passed into those functions
so invalid values never reach startBackgroundTimer or runForegroundTimer.
- Around line 90-95: The runForegroundTimer function currently takes five
positional args (durationMs, title, notify, say, repeat); refactor it to accept
a single options object (e.g., { durationMs, title, notify, say, repeat }) and
update the implementation to destructure those properties, and update all other
timer functions in this module with 3+ params likewise (the other timer APIs in
this file that mirror runForegroundTimer). Also update every call site to pass
the options object (or add a small compatibility wrapper that accepts the old
positional signature and converts to the options object) so behavior remains
unchanged while satisfying the 3+ parameter rule.

In `@src/transcribe/index.ts`:
- Around line 55-72: The helpers toSRT and toVTT currently emit plain untimed
text when result.segments is missing; update them to either synthesize a single
timed cue using result.duration or fail fast: if no segments and result.duration
is present, create one cue from 0 to result.duration (use formatTimestamp for
the end time, "0" or formatTimestamp(0) for the start), for SRT produce a
numbered cue "1\nstart --> end\n{text.trim()}" and for VTT include the "WEBVTT"
header plus the single cue, otherwise throw/return an error indicating missing
timestamps so callers requesting "srt"/"vtt" don't get invalid subtitle files;
reference functions toSRT and toVTT and the
TranscriptionResult.{segments,duration} fields when making the change.

In `@src/utils/macos/voice-memos.ts`:
- Around line 216-238: Update parseTsrpPayload to use strict RFC‑8259 JSON
parsing by passing the strict option into SafeJSON.parse; specifically, replace
the call to SafeJSON.parse(json) inside parseTsrpPayload with
SafeJSON.parse(json, { strict: true }) so the function still validates the same
attributedString/string + runs patterns but rejects non‑RFC8259 input.
- Around line 123-147: rowToMemo currently calls hasTranscript(filePath) for
every memo which forces reading entire audio files during listMemos; remove that
eager call and instead either (A) set hasTranscript to false/undefined in
rowToMemo and implement a separate memoHasTranscript(filePath) function that
performs a lightweight check (e.g., check for companion transcript files or
metadata rather than reading audio) to be called on demand, or (B) add an
optional parameter to listMemos/includeTranscript flag so callers can opt into
computing transcripts and only then invoke hasTranscript; update usages of
rowToMemo, listMemos, and any consumers to use the new on-demand
memoHasTranscript or the flag-based path.

In `@src/youtube/commands/transcribe.ts`:
- Around line 266-272: The fallback branch builds a CaptionResult with segments:
[] which loses timing data and breaks subtitle outputs; update the fallback in
transcribe.ts to reuse the timing/segments returned by transcribeAudio() (or any
variable holding its result) when available instead of forcing segments: [], so
that CaptionResult (used by formatOutput) contains real segment timings, or
alternatively throw/reject when format is a subtitle type (srt/vtt) if no timing
data exists; locate the creation of the result object (CaptionResult) and the
call site where transcription/text is set and patch it to copy the segments from
the transcribeAudio() response before calling formatOutput.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 93e76b1a-8b93-44a1-9ea8-a4934f5fdd17

📥 Commits

Reviewing files that changed from the base of the PR and between 43a053c and 7857437.

📒 Files selected for processing (19)
  • src/ai/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/macos/index.ts
  • src/timer/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/LanguageDetector.test.ts
  • src/utils/ai/LanguageDetector.ts
  • src/utils/ai/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
  • src/utils/audio/converter.test.ts
  • src/utils/audio/converter.ts
  • src/utils/macos/MacOS.ts
  • src/utils/macos/index.ts
  • src/utils/macos/system-settings.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/terminal.ts
  • src/youtube/commands/transcribe.ts
  • src/zsh/features/dotdotdot.ts

@genesiscz
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

♻️ Duplicate comments (19)
src/timer/index.ts (5)

90-96: 🛠️ Refactor suggestion | 🟠 Major

Multi-argument functions should use object parameters.

These functions have 5 positional parameters, which violates the coding guideline for 3+ parameters.

♻️ Proposed refactor
+interface TimerRunOptions {
+    durationMs: number;
+    title: string;
+    notify: boolean;
+    say: boolean;
+    repeat: number;
+}
+
-async function runForegroundTimer(
-    durationMs: number,
-    title: string,
-    notify: boolean,
-    say: boolean,
-    repeat: number
-): Promise<void> {
+async function runForegroundTimer({
+    durationMs,
+    title,
+    notify,
+    say,
+    repeat,
+}: TimerRunOptions): Promise<void> {

Apply similar refactoring to fireCompletionActions and startBackgroundTimer.

As per coding guidelines: "Use object parameters for functions with 3+ parameters or optional parameters".

Also applies to: 137-143, 173-179

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` around lines 90 - 96, Refactor the multi-argument
functions to accept a single options object: replace the positional parameters
of runForegroundTimer(durationMs, title, notify, say, repeat) with a typed
options parameter (e.g., RunForegroundTimerOptions) and update the function body
to destructure/default those fields; do the same for fireCompletionActions and
startBackgroundTimer (create corresponding options types/interfaces), update all
call sites to pass an object with the named properties, and preserve return
types and behavior (including defaults and any optional parameters).

261-264: ⚠️ Potential issue | 🟠 Major

Cleanup write should use config lock for consistency.

listTimers writes timer state without withConfigLock, while other mutators (addActiveTimer, removeActiveTimer) are locked. This can cause lost updates under concurrent timer processes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` around lines 261 - 264, The write in the listTimers flow
updates data.timers directly and calls saveActiveTimers without using the same
withConfigLock used by addActiveTimer/removeActiveTimer; wrap the mutation and
save in withConfigLock to acquire the config lock before setting data.timers =
alive and calling saveActiveTimers (matching the locking pattern of
addActiveTimer/removeActiveTimer) so concurrent timer processes can't lose
updates.

514-514: 🧹 Nitpick | 🔵 Trivial

Remove obvious inline comment.

The comment restates what the conditional makes clear.

As per coding guidelines: "Do not add obvious comments that restate what the code already says".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` at line 514, Remove the redundant inline comment "//
Handle internal background runner" at the indicated location in
src/timer/index.ts; the conditional or code block already conveys that intent,
so delete that comment and ensure no other duplicate/obvious comments remain
near the internal background runner conditional or surrounding function so the
code follows the commenting guidelines.

228-231: ⚠️ Potential issue | 🟠 Major

Background repeat cycles don't persist progress.

The loop advances cycles but never updates currentCycle or endTime in storage. timer list will show stale values for repeat timers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` around lines 228 - 231, The repeat loop advances cycles
but never persists progress: inside the for loop in the timer routine, update
the stored timer state (set currentCycle and endTime) on each iteration so
`timer list` reflects progress; specifically, after incrementing/starting a
cycle (and before awaiting the duration or after resolving the timeout) write
the new currentCycle and compute/persist the new endTime into storage, and
ensure this persistence happens alongside the existing call to
fireCompletionActions (refer to currentCycle, endTime, and
fireCompletionActions) so repeat timers show up-to-date values.

493-493: ⚠️ Potential issue | 🟠 Major

Validate --repeat in non-interactive CLI mode.

Interactive mode validates repeat (Lines 425-431), but direct CLI mode accepts options.repeat without validation. Invalid values like 0, negative, or NaN can slip through.

🛡️ Proposed fix
             const repeat = options.repeat ?? 1;
+
+            if (Number.isNaN(repeat) || repeat < 1 || !Number.isInteger(repeat)) {
+                console.log(pc.red(`Invalid repeat value: ${String(options.repeat)}`));
+                console.log(pc.dim("Repeat must be a positive integer."));
+                process.exit(1);
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` at line 493, The non-interactive CLI path sets const
repeat = options.repeat ?? 1 without validation; replicate the interactive
validation logic (Lines ~425-431) for options.repeat by parsing it to a
number/integer, ensuring it's finite and >= 1 (reject 0, negatives, NaN), and
either normalize to the default 1 or throw/return an error consistent with the
interactive path; update the assignment that creates repeat (the const repeat =
...) to perform this check (use Number.parseInt/Number.isFinite/Number.isInteger
or equivalent) so invalid CLI values cannot slip through.
src/utils/macos/notifications.ts (2)

145-150: ⚠️ Potential issue | 🟠 Major

Check exit code in sendViaTerminalNotifier.

The function returns true immediately after spawning without verifying the notification was actually sent. Consider awaiting the process and checking its exit code.

🔧 Proposed fix
-function sendViaTerminalNotifier(bin: string, opts: NotificationOptions): boolean {
+async function sendViaTerminalNotifier(bin: string, opts: NotificationOptions): Promise<boolean> {
     // ... args construction ...
     try {
-        Bun.spawn(args, { stdout: "ignore", stderr: "ignore" });
-        return true;
+        const proc = Bun.spawn(args, { stdout: "ignore", stderr: "ignore" });
+        return (await proc.exited) === 0;
     } catch {
         return false;
     }
 }

Then update the caller at Line 184 to await sendViaTerminalNotifier(...).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/notifications.ts` around lines 145 - 150, The function
sendViaTerminalNotifier currently just spawns a Bun process and returns true
without verifying success; change it to await the spawned process (capture the
process returned by Bun.spawn), await its completion, check the exit code
(return true only if exitCode === 0), and propagate/log any non-zero exit code
or thrown errors (return false on failure). Also update the caller that invokes
sendViaTerminalNotifier to await the promise so the result is used correctly.

88-99: ⚠️ Potential issue | 🟠 Major

Check exit code before caching terminal-notifier path.

Bun.spawn() does not throw on non-zero exit codes. The code awaits proc.exited but never checks the result, so a failing -help invocation (e.g., binary corruption) would still cache and return the candidate.

🔧 Proposed fix
                 const proc = Bun.spawn([candidate, "-help"], {
                     stdout: "ignore",
                     stderr: "ignore",
                 });
-                await proc.exited;
+
+                if ((await proc.exited) !== 0) {
+                    continue;
+                }

                 await storage.setConfigValue("terminalNotifierPath", candidate);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/notifications.ts` around lines 88 - 99, The try block
currently awaits proc.exited but never validates the exit result, so update the
logic that spawns the candidate binary (the proc created for candidate with
"-help") to inspect the resolved exit information (e.g., success/exitCode) from
proc.exited and only call storage.setConfigValue("terminalNotifierPath",
candidate) and logger.debug(`Resolved terminal-notifier: ${candidate}`) when the
process exited successfully (exit code 0); if the process failed, treat it as a
non-usable candidate (do not cache or return it) and allow the loop to continue
or throw as appropriate.
src/macos/commands/voice-memos/index.ts (3)

46-46: ⚠️ Potential issue | 🟡 Minor

Add radix parameter to parseInt — pipeline failure.

The CI pipeline explicitly flags this line for missing radix parameter.

🔧 Proposed fix
     vm.command("transcribe")
         .description("Transcribe a voice memo (tsrp first, then AI fallback)")
-        .argument("[id]", "Memo ID (omit for --all)", (v) => parseInt(v))
+        .argument("[id]", "Memo ID (omit for --all)", (v) => parseInt(v, 10))
         .option("--all", "Transcribe all memos")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/macos/commands/voice-memos/index.ts` at line 46, The .argument call's
parse callback uses parseInt without a radix which CI flags; update the argument
definition (the .argument("[id]", "Memo ID (omit for --all)", (v) =>
parseInt(v)) call) to pass an explicit radix (e.g., parseInt(v, 10)) and ensure
the callback handles undefined/null inputs if needed so the Memo ID parsing is
deterministic.

38-38: ⚠️ Potential issue | 🟡 Minor

Add radix parameter to parseInt.

🔧 Proposed fix
     vm.command("export")
         .description("Export a voice memo to a destination")
-        .argument("<id>", "Memo ID", parseInt)
+        .argument("<id>", "Memo ID", (v) => parseInt(v, 10))
         .argument("[dest]", "Destination directory", ".")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/macos/commands/voice-memos/index.ts` at line 38, The call to parseInt
used as the argument parser (.argument("<id>", "Memo ID", parseInt)) omits the
radix which can lead to inconsistent parsing; update the parser to pass an
explicit radix by replacing parseInt with a wrapper that calls parseInt(value,
10) (e.g., value => parseInt(value, 10)) so the "<id>" argument is always parsed
as base-10.

31-31: ⚠️ Potential issue | 🟡 Minor

Add radix parameter to parseInt.

The pipeline fails because parseInt is missing the radix parameter. This also applies to lines 38 and 46.

🔧 Proposed fix
     vm.command("play")
         .description("Play a voice memo")
-        .argument("<id>", "Memo ID", parseInt)
+        .argument("<id>", "Memo ID", (v) => parseInt(v, 10))
         .action(async (id: number) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/macos/commands/voice-memos/index.ts` at line 31, The .argument calls
currently pass the global parseInt without a radix (e.g., .argument("<id>",
"Memo ID", parseInt), and two other similar occurrences), which can produce
incorrect parsing; update each of these argument definitions to use a parsing
function that explicitly supplies radix 10 (for example, replace the bare
parseInt reference with a small wrapper that calls parseInt(value, 10) or an
equivalent conversion function) so the Memo ID and the other numeric arguments
are reliably parsed as base-10 integers.
src/utils/macos/voice-memos.ts (2)

123-146: ⚠️ Potential issue | 🟠 Major

rowToMemo() turns simple listing into a full-audio scan.

hasTranscript(filePath) reads the entire recording to probe for tsrp, so listMemos() and searchMemos() now scale with total audio bytes rather than memo count. Defer transcript detection until it's explicitly requested, or replace this with a lightweight metadata check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/voice-memos.ts` around lines 123 - 146, The rowToMemo
function currently calls hasTranscript(filePath) eagerly which forces a
full-audio scan for every memo; update rowToMemo (and any callers like
listMemos/searchMemos) to stop invoking hasTranscript and instead set a
lightweight flag or placeholder (e.g., transcriptChecked: false or
transcriptAvailableMetadata: <metadata lookup>) and defer running hasTranscript
until an explicit transcript request (e.g., a new getMemoTranscript or when
detail view is opened); locate rowToMemo, hasTranscript, and callers
listMemos/searchMemos and change the returned VoiceMemo shape to avoid expensive
I/O at listing time, providing a separate on-demand function to perform the full
scan.

216-218: ⚠️ Potential issue | 🟠 Major

Parse embedded tsrp JSON in strict mode.

This payload comes from a file-format boundary, not user-authored config, so lenient parsing can mask malformed transcript data.

🔧 Suggested fix
-        const data = SafeJSON.parse(json);
+        const data = SafeJSON.parse(json, { strict: true });

Based on learnings, use SafeJSON.parse(text, { strict: true }) at non-config JSON boundaries and reserve the lenient parser for user-authored config files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/voice-memos.ts` around lines 216 - 218, The parseTsrpPayload
function is using lenient JSON parsing; update the SafeJSON.parse call in
parseTsrpPayload to use strict mode by passing { strict: true } so embedded tsrp
payloads are parsed strictly (retain existing try/catch and return behavior).
Ensure you only change the SafeJSON.parse invocation inside parseTsrpPayload and
do not alter parsing behavior elsewhere (user config parsers remain lenient).
src/transcribe/index.ts (1)

55-83: ⚠️ Potential issue | 🟠 Major

Don't emit untimed text as srt/vtt.

When result.segments is missing, toSRT() returns raw text and toVTT() returns a header plus raw text. Those are not valid subtitle files, so callers asking for subtitle output get malformed data. Either synthesize a single cue from result.duration or reject subtitle formats when timing data is unavailable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/transcribe/index.ts` around lines 55 - 83, When segments are missing,
don't return raw text; instead synthesize a single timed cue using the
transcription's duration: update toSRT and toVTT to create one cue from start 0
to result.duration (or fallback 0 if duration is absent) using formatTimestamp
for start/end and include the proper SRT/VTT header and cue formatting;
reference symbols: toSRT, toVTT, formatTimestamp, and
TranscriptionResult.duration/segments so callers always receive valid subtitle
files.
src/youtube/commands/transcribe.ts (2)

392-401: ⚠️ Potential issue | 🟠 Major

Use process.exitCode here so the finally cleanup still runs.

process.exit(1) terminates before the outer finally unlinks audioPath, so failed transcriptions leak the downloaded temp file.

🔧 Suggested fix
     } catch (error) {
         spinner.stop(pc.red("Transcription failed"));
         const message = error instanceof Error ? error.message : String(error);
         p.log.error(message);
         logger.error({ error, url }, "YouTube transcription failed");
-        process.exit(1);
+        process.exitCode = 1;
+        return;
     } finally {
In Node.js or Bun, does calling process.exit() inside a catch block prevent the enclosing finally block from running?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/youtube/commands/transcribe.ts` around lines 392 - 401, The catch block
currently calls process.exit(1) which can prevent the outer finally cleanup (the
unlink of audioPath) from running; instead set process.exitCode = 1 and remove
the process.exit call so the finally block executes and the temporary file is
unlinked—update the catch handling around spinner.stop, p.log.error and
logger.error (the block that currently calls process.exit) to assign
process.exitCode = 1 and let execution continue to the existing finally that
checks audioPath and unlinks it.

370-379: ⚠️ Potential issue | 🟠 Major

Don't coerce missing timing into an empty subtitle track.

transcription.segments ?? [] erases the distinction between “no timing data” and “an actual empty transcript”. For --format srt/vtt, this currently produces an empty file or just WEBVTT even though transcription.text exists. Preserve the missing-timing state here and either synthesize a single cue from transcription.duration or fail fast for subtitle formats.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/youtube/commands/transcribe.ts` around lines 370 - 379, The current
construction of CaptionResult coerces missing timing into an empty array by
using transcription.segments ?? [], which loses the distinction between “no
timing data” and an actual empty transcript; change the build of CaptionResult
(the result variable in transcribe command) to keep segments as undefined/null
when transcription.segments is missing (i.e., assign segments:
transcription.segments instead of transcription.segments ?? []), and then update
downstream handling for subtitle formats (srt/vtt) to either synthesize a single
cue using transcription.duration when timing is absent or fail-fast with a clear
error; look for references to CaptionResult, result, and the code paths that
render --format srt/vtt and implement the synthesize-or-error behavior there.
src/utils/macos/tts.ts (2)

75-92: ⚠️ Potential issue | 🟠 Major

listVoicesStructured() is only returning one voice per language.

getVoiceMap() intentionally keeps the first voice for each lang, but listVoicesStructured() exposes that reduced map as the full structured list. Callers cannot see secondary voices or regional variants at all. Keep a full VoiceInfo[] for listing, and reserve the Map<string, VoiceInfo> only for language fallback.

Also applies to: 263-265

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/tts.ts` around lines 75 - 92, getVoiceMap() currently
deduplicates voices by language using the Map<string, VoiceInfo>, which causes
listVoicesStructured() to expose only the first voice per language and hides
secondary/regional variants; change getVoiceMap() (and related logic in
listVoicesStructured()) to build and return a full VoiceInfo[] for listing while
still maintaining a separate Map<string, VoiceInfo> only for language-fallback
lookup: parse all lines into an array (e.g., voices: VoiceInfo[]), push every
parsed voice into voices, then if the fallback map (map) does not yet have the
language key set map.set(lang, voice) so the map keeps first-per-language but
the returned structured list uses the full voices array so callers see all
voices and variants. Ensure functions listVoicesStructured() and getVoiceMap()
reference the full array for output and the map only for fallback selection.

122-127: ⚠️ Potential issue | 🔴 Critical

Auto-creating appMute[app] = false breaks global mute.

With globalMute = true, the first muted speak({ app }) call writes an explicit false entry, so later calls for that app stop inheriting the global mute. Reads shouldn't persist defaults; only an explicit mute/unmute action should write appMute.

🔧 Suggested fix
     if (isMuted(config, options?.app)) {
         process.stderr.write("[say] muted\n");
-
-        // Auto-create app entry if it doesn't exist
-        if (options?.app && !(options.app in config.appMute)) {
-            config.appMute[options.app] = false;
-            await setConfig(config);
-        }
-
         return;
     }
-
-    // Auto-create app entry on first use
-    if (options?.app && !(options.app in config.appMute)) {
-        config.appMute[options.app] = false;
-        await setConfig(config);
-    }

Also applies to: 155-171

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/tts.ts` around lines 122 - 127, The isMuted logic is
currently auto-creating implicit entries and causing apps to stop inheriting
globalMute; update isMuted to only treat an app as muted/unmuted when there is
an explicit entry in config.appMute (use
Object.prototype.hasOwnProperty.call(config.appMute, app) or equivalent) and
otherwise return config.globalMute, and remove any reads that set
config.appMute[app] = false (including similar logic in the speak-related code
around lines 155-171) so reads never persist defaults—only explicit user
mute/unmute actions should write to config.appMute.
src/utils/ai/providers/AILocalProvider.ts (2)

31-33: ⚠️ Potential issue | 🟡 Minor

isAvailable() currently over-reports readiness.

Returning true unconditionally can report the provider as ready even when local model loading/download fails at runtime.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/ai/providers/AILocalProvider.ts` around lines 31 - 33, The
isAvailable() method in AILocalProvider currently always returns true and should
instead perform a real readiness check: update AILocalProvider.isAvailable to
verify the local model or runtime is actually usable (e.g., check model files
exist, validate model checksum, confirm any required subprocess/service is
running, or attempt a lightweight model init call) and return true only if those
checks succeed; ensure errors are caught and logged and that the method returns
false on failure so callers get an accurate availability signal.

226-231: ⚠️ Potential issue | 🔴 Critical

Cache cleanup path is built incorrectly (replace("/", "/") is a no-op).

This computes the wrong Hugging Face cache directory, so corrupted cache cleanup may silently miss the target and keep failing retries.

Proposed fix
                     if (cacheDir) {
                         const { rmSync } = await import("node:fs");
-                        const modelCacheDir = `${cacheDir}/${model.replace("/", "/")}`;
+                        const { join } = await import("node:path");
+                        const modelCacheDir = join(
+                            cacheDir,
+                            `models--${model.replace(/[\\/]/g, "--")}`
+                        );

                         try {
                             rmSync(modelCacheDir, { recursive: true, force: true });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/ai/providers/AILocalProvider.ts` around lines 226 - 231, Summary:
The cache cleanup builds modelCacheDir incorrectly because model.replace("/",
"/") is a no-op; fix by constructing the path with proper path separators. In
AILocalProvider where cacheDir, modelCacheDir, model and rmSync are used,
replace the noop replace call with a correct path construction (e.g., use
node:path join or replace all forward slashes with the platform path separator)
so modelCacheDir = path.join(cacheDir, model) or model.replace(/\//g, path.sep);
keep the existing rmSync(modelCacheDir, { recursive: true, force: true }) call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/e2e/youtube.e2e.test.ts`:
- Around line 53-62: The test "fetches captions as JSON" uses SafeJSON.parse
without strict mode; update the parsing call in that test to use strict parsing
by calling SafeJSON.parse(clean.slice(jsonStart), { strict: true }) so the CLI
output is validated strictly at the subprocess boundary (keep the surrounding
logic: runTool, stripAnsi, jsonStart detection, and subsequent expects).

In `@src/macos/commands/voice-memos/index.ts`:
- Around line 92-100: formatMemoDate currently hardcodes the "en-US" locale
which prevents using the user's system locale; update the function
(formatMemoDate) to use the runtime/system locale instead (e.g. pass undefined
as the locale to toLocaleDateString or derive the locale via
Intl.DateTimeFormat().resolvedOptions().locale) so dates are formatted according
to the user's environment while keeping the same options for
year/month/day/hour/minute.

In `@src/say/index.ts`:
- Around line 248-254: In the validate function in src/say/index.ts, replace the
global isNaN check with Number.isNaN to avoid coercion: after computing n =
parseFloat(value ?? ""), use Number.isNaN(n) in the conditional (and keep the
existing n < 0 || n > 1 checks) so the validation correctly detects NaN without
type-coercion side effects; update the conditional that currently reads isNaN(n)
|| n < 0 || n > 1 to use Number.isNaN(n) instead.

In `@src/transcribe/index.ts`:
- Around line 337-349: The CLI currently accepts arbitrary --format values which
can lead to undefined being passed into formatOutput(), copyToClipboard(),
Bun.write(), or stdout; add validation in the action handler (the anonymous
async function that calls runTranscription) to check opts.format against the
allowed set ["text","json","srt","vtt"] and reject any unknown value before
calling runTranscription(file, opts) by logging a clear error and exiting with a
non-zero status (or throwing), so runTranscription/formatOutput never receive an
unsupported format.

In `@src/utils/ai/ModelManager.ts`:
- Around line 10-30: The listDownloaded function is marked async but uses only
synchronous ops; change it to a synchronous method by removing the async
modifier and updating its return type from Promise<Array<{ modelId: string;
sizeBytes: number }>> to Array<{ modelId: string; sizeBytes: number }>, or
alternatively make getDirSize asynchronous and await it inside
listDownloaded—pick one approach and update the signature and any callers
accordingly (refer to listDownloaded and getDirSize to locate the code and
ensure consistent return types).
- Around line 32-46: The download method currently uses
pipeline("feature-extraction", modelId, ...) to trigger a model cache download;
replace that with the more direct HF API calls such as
AutoModel.from_pretrained(modelId) (and AutoTokenizer.from_pretrained or
AutoProcessor.from_pretrained as appropriate) so you only fetch the model
artifacts without instantiating a full pipeline. In the async download(modelId,
...) function, import the proper classes from "@huggingface/transformers", call
the appropriate from_pretrained for the model/tokenizer/processor, await
completion, then dispose/unload any returned objects if needed, and keep the
existing logger.info messages (logger.info(`Downloading model: ${modelId}`) and
logger.info(`Model downloaded: ${modelId}`)).

In `@src/utils/ai/providers/AICloudProvider.ts`:
- Around line 45-54: In transcribe (AICloudProvider.transcribe) you currently
write the audio Buffer to a temp file named *.wav which mislabels non-WAV
inputs; change the logic to either (a) detect the buffer's actual content
type/extension (e.g., via magic bytes or an incoming options.extension) and
write temp files with that real extension before calling
transcriptionManager.transcribeAudio, or (b) transcode the buffer to a true WAV
(using the existing transcode utility or a small conversion step) and then write
a .wav; update the tempPath creation and the Bun.write call so
TranscriptionManager.transcribeAudio receives a correctly-typed file rather than
a falsely-named .wav.
- Around line 56-59: The current return in AICloudProvider (the method that now
returns { text: result.text, duration: result.duration }) strips needed fields;
change it to return the full TranscriptionResult (include result.segments and
result.language) by returning the complete result object (or an object that
preserves segments, language, text, duration) so callers like
src/transcribe/index.ts and src/youtube/commands/transcribe.ts receive timings
and detected language; ensure the return type matches TranscriptionResult and
adjust any typing if necessary.

In `@src/utils/ai/providers/AILocalProvider.ts`:
- Line 180: The getPipeline method signature should be changed from
getPipeline(task: PipelineType, model: string, onProgress?: OnProgress) to
accept a single object param (e.g. getPipeline({ task, model, onProgress }: {
task: PipelineType; model: string; onProgress?: OnProgress })) to follow repo
conventions; update the implementation to destructure that object and keep
return type Promise<PipelineInstance>, then update every call site that invokes
getPipeline(...) to pass an object with named properties (including tests and
any internal usages) to avoid ordering bugs and preserve optional onProgress
handling.
- Around line 147-154: The catch block around this.getPipeline("translation",
model) in AILocalProvider.ts currently converts all failures into an
"Unsupported language pair" Error; change it to only throw the language-pair
error when the getPipeline failure indicates the model/pair is actually missing
(e.g., check the thrown error message/type or use a sentinel from getPipeline)
and otherwise rethrow the original error so network/cache/runtime failures are
preserved; update the logic in the try/catch around getPipeline (and any code
that sets pipe) to inspect the caught error and either throw the specific
Unsupported language pair message or rethrow the original error with its
details.

In `@src/utils/ai/tasks/Transcriber.ts`:
- Around line 103-107: In the Transcriber.ts code block that updates timeOffset,
the branch that handles a missing result.duration currently assigns the last
segment end directly (timeOffset = result.segments[result.segments.length -
1].end) which resets the running offset; change that to add the chunk-local
segment end to the existing timeOffset (timeOffset +=
result.segments[result.segments.length - 1].end) so timings accumulate across
chunks; locate this logic in the method that processes transcription results
(the variable timeOffset and the result.duration / result.segments checks) and
replace the assignment with an additive update.

In `@src/utils/json.test.ts`:
- Around line 67-68: The test is coercing parsed primitives with Number(...) and
String(...), which can hide type regressions; update the assertions to check the
raw parsed values returned by parseJSON instead of coercing them — e.g., call
parseJSON<number>("123") and assert the result is 123, and call
parseJSON<string>('"hello"') and assert the result is "hello" (you can keep the
TypeScript generics on parseJSON<number>/parseJSON<string> but remove the
Number()/String() wrappers so the test fails if the runtime type is wrong).

---

Duplicate comments:
In `@src/macos/commands/voice-memos/index.ts`:
- Line 46: The .argument call's parse callback uses parseInt without a radix
which CI flags; update the argument definition (the .argument("[id]", "Memo ID
(omit for --all)", (v) => parseInt(v)) call) to pass an explicit radix (e.g.,
parseInt(v, 10)) and ensure the callback handles undefined/null inputs if needed
so the Memo ID parsing is deterministic.
- Line 38: The call to parseInt used as the argument parser (.argument("<id>",
"Memo ID", parseInt)) omits the radix which can lead to inconsistent parsing;
update the parser to pass an explicit radix by replacing parseInt with a wrapper
that calls parseInt(value, 10) (e.g., value => parseInt(value, 10)) so the
"<id>" argument is always parsed as base-10.
- Line 31: The .argument calls currently pass the global parseInt without a
radix (e.g., .argument("<id>", "Memo ID", parseInt), and two other similar
occurrences), which can produce incorrect parsing; update each of these argument
definitions to use a parsing function that explicitly supplies radix 10 (for
example, replace the bare parseInt reference with a small wrapper that calls
parseInt(value, 10) or an equivalent conversion function) so the Memo ID and the
other numeric arguments are reliably parsed as base-10 integers.

In `@src/timer/index.ts`:
- Around line 90-96: Refactor the multi-argument functions to accept a single
options object: replace the positional parameters of
runForegroundTimer(durationMs, title, notify, say, repeat) with a typed options
parameter (e.g., RunForegroundTimerOptions) and update the function body to
destructure/default those fields; do the same for fireCompletionActions and
startBackgroundTimer (create corresponding options types/interfaces), update all
call sites to pass an object with the named properties, and preserve return
types and behavior (including defaults and any optional parameters).
- Around line 261-264: The write in the listTimers flow updates data.timers
directly and calls saveActiveTimers without using the same withConfigLock used
by addActiveTimer/removeActiveTimer; wrap the mutation and save in
withConfigLock to acquire the config lock before setting data.timers = alive and
calling saveActiveTimers (matching the locking pattern of
addActiveTimer/removeActiveTimer) so concurrent timer processes can't lose
updates.
- Line 514: Remove the redundant inline comment "// Handle internal background
runner" at the indicated location in src/timer/index.ts; the conditional or code
block already conveys that intent, so delete that comment and ensure no other
duplicate/obvious comments remain near the internal background runner
conditional or surrounding function so the code follows the commenting
guidelines.
- Around line 228-231: The repeat loop advances cycles but never persists
progress: inside the for loop in the timer routine, update the stored timer
state (set currentCycle and endTime) on each iteration so `timer list` reflects
progress; specifically, after incrementing/starting a cycle (and before awaiting
the duration or after resolving the timeout) write the new currentCycle and
compute/persist the new endTime into storage, and ensure this persistence
happens alongside the existing call to fireCompletionActions (refer to
currentCycle, endTime, and fireCompletionActions) so repeat timers show
up-to-date values.
- Line 493: The non-interactive CLI path sets const repeat = options.repeat ?? 1
without validation; replicate the interactive validation logic (Lines ~425-431)
for options.repeat by parsing it to a number/integer, ensuring it's finite and
>= 1 (reject 0, negatives, NaN), and either normalize to the default 1 or
throw/return an error consistent with the interactive path; update the
assignment that creates repeat (the const repeat = ...) to perform this check
(use Number.parseInt/Number.isFinite/Number.isInteger or equivalent) so invalid
CLI values cannot slip through.

In `@src/transcribe/index.ts`:
- Around line 55-83: When segments are missing, don't return raw text; instead
synthesize a single timed cue using the transcription's duration: update toSRT
and toVTT to create one cue from start 0 to result.duration (or fallback 0 if
duration is absent) using formatTimestamp for start/end and include the proper
SRT/VTT header and cue formatting; reference symbols: toSRT, toVTT,
formatTimestamp, and TranscriptionResult.duration/segments so callers always
receive valid subtitle files.

In `@src/utils/ai/providers/AILocalProvider.ts`:
- Around line 31-33: The isAvailable() method in AILocalProvider currently
always returns true and should instead perform a real readiness check: update
AILocalProvider.isAvailable to verify the local model or runtime is actually
usable (e.g., check model files exist, validate model checksum, confirm any
required subprocess/service is running, or attempt a lightweight model init
call) and return true only if those checks succeed; ensure errors are caught and
logged and that the method returns false on failure so callers get an accurate
availability signal.
- Around line 226-231: Summary: The cache cleanup builds modelCacheDir
incorrectly because model.replace("/", "/") is a no-op; fix by constructing the
path with proper path separators. In AILocalProvider where cacheDir,
modelCacheDir, model and rmSync are used, replace the noop replace call with a
correct path construction (e.g., use node:path join or replace all forward
slashes with the platform path separator) so modelCacheDir = path.join(cacheDir,
model) or model.replace(/\//g, path.sep); keep the existing
rmSync(modelCacheDir, { recursive: true, force: true }) call.

In `@src/utils/macos/notifications.ts`:
- Around line 145-150: The function sendViaTerminalNotifier currently just
spawns a Bun process and returns true without verifying success; change it to
await the spawned process (capture the process returned by Bun.spawn), await its
completion, check the exit code (return true only if exitCode === 0), and
propagate/log any non-zero exit code or thrown errors (return false on failure).
Also update the caller that invokes sendViaTerminalNotifier to await the promise
so the result is used correctly.
- Around line 88-99: The try block currently awaits proc.exited but never
validates the exit result, so update the logic that spawns the candidate binary
(the proc created for candidate with "-help") to inspect the resolved exit
information (e.g., success/exitCode) from proc.exited and only call
storage.setConfigValue("terminalNotifierPath", candidate) and
logger.debug(`Resolved terminal-notifier: ${candidate}`) when the process exited
successfully (exit code 0); if the process failed, treat it as a non-usable
candidate (do not cache or return it) and allow the loop to continue or throw as
appropriate.

In `@src/utils/macos/tts.ts`:
- Around line 75-92: getVoiceMap() currently deduplicates voices by language
using the Map<string, VoiceInfo>, which causes listVoicesStructured() to expose
only the first voice per language and hides secondary/regional variants; change
getVoiceMap() (and related logic in listVoicesStructured()) to build and return
a full VoiceInfo[] for listing while still maintaining a separate Map<string,
VoiceInfo> only for language-fallback lookup: parse all lines into an array
(e.g., voices: VoiceInfo[]), push every parsed voice into voices, then if the
fallback map (map) does not yet have the language key set map.set(lang, voice)
so the map keeps first-per-language but the returned structured list uses the
full voices array so callers see all voices and variants. Ensure functions
listVoicesStructured() and getVoiceMap() reference the full array for output and
the map only for fallback selection.
- Around line 122-127: The isMuted logic is currently auto-creating implicit
entries and causing apps to stop inheriting globalMute; update isMuted to only
treat an app as muted/unmuted when there is an explicit entry in config.appMute
(use Object.prototype.hasOwnProperty.call(config.appMute, app) or equivalent)
and otherwise return config.globalMute, and remove any reads that set
config.appMute[app] = false (including similar logic in the speak-related code
around lines 155-171) so reads never persist defaults—only explicit user
mute/unmute actions should write to config.appMute.

In `@src/utils/macos/voice-memos.ts`:
- Around line 123-146: The rowToMemo function currently calls
hasTranscript(filePath) eagerly which forces a full-audio scan for every memo;
update rowToMemo (and any callers like listMemos/searchMemos) to stop invoking
hasTranscript and instead set a lightweight flag or placeholder (e.g.,
transcriptChecked: false or transcriptAvailableMetadata: <metadata lookup>) and
defer running hasTranscript until an explicit transcript request (e.g., a new
getMemoTranscript or when detail view is opened); locate rowToMemo,
hasTranscript, and callers listMemos/searchMemos and change the returned
VoiceMemo shape to avoid expensive I/O at listing time, providing a separate
on-demand function to perform the full scan.
- Around line 216-218: The parseTsrpPayload function is using lenient JSON
parsing; update the SafeJSON.parse call in parseTsrpPayload to use strict mode
by passing { strict: true } so embedded tsrp payloads are parsed strictly
(retain existing try/catch and return behavior). Ensure you only change the
SafeJSON.parse invocation inside parseTsrpPayload and do not alter parsing
behavior elsewhere (user config parsers remain lenient).

In `@src/youtube/commands/transcribe.ts`:
- Around line 392-401: The catch block currently calls process.exit(1) which can
prevent the outer finally cleanup (the unlink of audioPath) from running;
instead set process.exitCode = 1 and remove the process.exit call so the finally
block executes and the temporary file is unlinked—update the catch handling
around spinner.stop, p.log.error and logger.error (the block that currently
calls process.exit) to assign process.exitCode = 1 and let execution continue to
the existing finally that checks audioPath and unlinks it.
- Around line 370-379: The current construction of CaptionResult coerces missing
timing into an empty array by using transcription.segments ?? [], which loses
the distinction between “no timing data” and an actual empty transcript; change
the build of CaptionResult (the result variable in transcribe command) to keep
segments as undefined/null when transcription.segments is missing (i.e., assign
segments: transcription.segments instead of transcription.segments ?? []), and
then update downstream handling for subtitle formats (srt/vtt) to either
synthesize a single cue using transcription.duration when timing is absent or
fail-fast with a clear error; look for references to CaptionResult, result, and
the code paths that render --format srt/vtt and implement the
synthesize-or-error behavior there.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 51f438bd-66e2-482f-9fc3-3a039fedb2e7

📥 Commits

Reviewing files that changed from the base of the PR and between 7857437 and 261a829.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (36)
  • .gitignore
  • package.json
  • src/e2e/ai.e2e.test.ts
  • src/e2e/benchmark.e2e.test.ts
  • src/e2e/helpers.ts
  • src/e2e/notify.e2e.test.ts
  • src/e2e/say.e2e.test.ts
  • src/e2e/timer.e2e.test.ts
  • src/e2e/transcribe.e2e.test.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/e2e/youtube.e2e.test.ts
  • src/macos/commands/voice-memos/index.ts
  • src/say/index.ts
  • src/timer/index.ts
  • src/timer/timer.test.ts
  • src/transcribe/index.ts
  • src/transcribe/transcribe.test.ts
  • src/utils/ai/LanguageDetector.test.ts
  • src/utils/ai/LanguageDetector.ts
  • src/utils/ai/ModelManager.ts
  • src/utils/ai/index.ts
  • src/utils/ai/providers/AICloudProvider.ts
  • src/utils/ai/providers/AIDarwinKitProvider.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/tasks/Summarizer.ts
  • src/utils/ai/tasks/Transcriber.ts
  • src/utils/ai/types.ts
  • src/utils/json.test.ts
  • src/utils/macos/darwinkit.test.ts
  • src/utils/macos/index.ts
  • src/utils/macos/notifications.ts
  • src/utils/macos/tts.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/terminal.test.ts
  • src/youtube/commands/transcribe.test.ts
  • src/youtube/commands/transcribe.ts
💤 Files with no reviewable changes (1)
  • src/utils/macos/darwinkit.test.ts

genesiscz added a commit that referenced this pull request Mar 18, 2026
- fix(ai): correct HuggingFace cache path construction (no-op replace bug)
- fix(youtube): prevent process.exit() from skipping finally cleanup
- fix(youtube): use template literals for string concatenation
- fix(ai): use template literal in HF token log
- fix(timer): refactor to options objects, add duration/repeat validation
- fix(timer): persist cycle progress in background repeat timers
- fix(timer): use withConfigLock for timer cleanup in list
- fix(voice-memos): add radix 10 to parseInt calls
- fix(audio): clean up temp WAV file on conversion error
- fix(audio): tighten test assertion for garbage input rejection
- fix(terminal): trim redundant JSDoc line
@genesiscz
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@genesiscz
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

♻️ Duplicate comments (3)
src/utils/terminal.ts (1)

1-5: 🛠️ Refactor suggestion | 🟠 Major

Trim the JSDoc block to non-obvious rationale only.

This comment block mostly restates behavior already clear from the function body and test-backed mapping logic.

✂️ Proposed cleanup
-/**
- * Detect the name of the current terminal application.
- * Checks CMUX_BUNDLE_ID first (cmux sets TERM_PROGRAM="" but has its own env),
- * then falls back to TERM_PROGRAM.
- */
 export function detectTerminalApp(): string {

As per coding guidelines, “Do not add obvious comments that restate what the code already says”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/terminal.ts` around lines 1 - 5, Remove the verbose JSDoc that
duplicates the code logic and replace it with a concise note that documents only
the non-obvious rationale: mention that CMUX_BUNDLE_ID is checked first because
cmux clears TERM_PROGRAM, and otherwise TERM_PROGRAM is used; keep a short
one-line summary like "Detect terminal name, preferring CMUX_BUNDLE_ID over
TERM_PROGRAM for cmux support." Update the JSDoc located above the terminal
detection function to this trimmed form and ensure the existing test-backed
mapping logic and env var names (CMUX_BUNDLE_ID, TERM_PROGRAM) remain referenced
only where necessary.
src/timer/index.ts (1)

383-397: ⚠️ Potential issue | 🟠 Major

Interactive duration validation still bypasses max-timeout guard.

This validator accepts any positive duration, but direct CLI mode already caps duration at MAX_TIMEOUT_MS. A very large value entered here can still flow into background mode and hit setTimeout overflow behavior.

Proposed fix
@@
-                    if (ms <= 0) {
-                        return "Invalid duration. Use formats like: 25m, 1h30m, 90s";
+                    if (ms <= 0 || ms > MAX_TIMEOUT_MS) {
+                        return "Invalid duration. Use formats like: 25m, 1h30m, 90s (max ~24.8 days)";
                     }
@@
-    if (durationMs <= 0) {
+    if (durationMs <= 0 || durationMs > MAX_TIMEOUT_MS) {
         const durationInput = await withCancel(
@@
         );
 
         durationMs = parseDuration(durationInput);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` around lines 383 - 397, The interactive duration
validator inside the withCancel(p.text(... validate(value) { ... })) currently
only checks for ms > 0 and can accept values above MAX_TIMEOUT_MS; update the
validator to parse the duration, then reject values that exceed MAX_TIMEOUT_MS
(use the MAX_TIMEOUT_MS constant) by returning a validation message like
"Duration exceeds maximum allowed X (e.g. 24h)"; ensure the validator only
accepts values where 0 < ms <= MAX_TIMEOUT_MS so the rest of the flow
(background mode / setTimeout) never receives an overflow-prone value.
src/macos/commands/voice-memos/index.ts (1)

186-189: ⚠️ Potential issue | 🟠 Major

--all mode does not perform AI fallback and --force is ineffective for batch transcription.

transcribeOne falls back to AI, but transcribeAll only reads embedded transcripts. In batch mode, --force still does not re-transcribe with AI.

Proposed fix
 async function transcribeAction(id: number | undefined, opts: TranscribeOpts): Promise<void> {
     if (opts.all) {
-        transcribeAll(opts.force ?? false);
+        await transcribeAll({ force: opts.force ?? false, lang: opts.lang });
         return;
     }
@@
-function transcribeAll(force: boolean): void {
+async function transcribeAll({
+    force,
+    lang,
+}: {
+    force: boolean;
+    lang?: string;
+}): Promise<void> {
@@
-        const transcript = extractTranscript(memo.path);
+        const transcript = !force ? extractTranscript(memo.path) : null;
 
         if (transcript) {
             transcribed++;
             p.log.success(`${memo.title}: ${transcript.text.slice(0, 80)}${transcript.text.length > 80 ? "..." : ""}`);
         } else {
-            noTranscript++;
+            await transcribeOne({ id: memo.id, force: true, lang });
+            transcribed++;
         }
     }

Also applies to: 259-296

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/macos/commands/voice-memos/index.ts` around lines 186 - 189,
transcribeAction's batch branch calls transcribeAll without performing the same
AI fallback or honoring --force; update transcribeAll (or its caller) so batch
transcription uses the same logic as transcribeOne: accept a force/fallback flag
and, for each memo, attempt embedded transcript first and if missing or
force=true then invoke the AI fallback path (or call transcribeOne per memo) so
--force re-triggers AI transcription and --all behaves consistently with single
transcribe behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/github/commands/issue.ts`:
- Line 617: Remove the redundant inline narration comments that simply restate
nearby code (e.g., the comment text "Resolve file path" and the similar comment
around the later occurrence). Locate the comments in
src/github/commands/issue.ts by searching for that exact comment string and any
adjacent one at the other flagged spot, and delete them so only meaningful,
non-obvious comments remain; do not change surrounding code or logic in the
functions that perform the path resolution.
- Around line 644-645: The current branch treats any defined options.save (even
false) as enabling saving because it checks options.save !== undefined; update
the guard so saving only happens when options.save is a string or truthy (not
when explicitly false). In the md/json handling where localDir is set
(reference: options.save and localDir in src/github/commands/issue.ts), change
the condition to require options.save !== false and either typeof options.save
=== "string" or Boolean(options.save) true before computing localDir, and skip
creating/writing the default directory when options.save === false.
- Around line 618-626: The current logic computes the directory for the output
file using a string "/" check which fails on Windows; replace that logic by
using Node's path.dirname (e.g., use path.dirname(filepath) or import dirname
from "path") to compute dir from filepath, then keep the existsSync(dir) /
mkdirSync(dir, { recursive: true }) flow so parent directories are created
correctly for both POSIX and Windows paths; update references in this block
where filepath/dir are used (options.output, options.save, join, existsSync,
mkdirSync) to use the dirname-derived dir.

In `@src/github/commands/pr.ts`:
- Around line 362-363: The code treats options.save === false as opting in;
change the guard so only true or a string enable saving: replace the current
branch that checks options.save !== undefined with a condition like
(options.save === true || typeof options.save === "string") and only then
compute localDir (using the string value or default join(process.cwd(),
".claude", "github")). Update references around options.save and localDir in the
pr command (e.g., prCommand handling) so an explicit false leaves saving
disabled.

In `@src/macos/commands/voice-memos/index.ts`:
- Line 1: The code currently reads the entire memo into memory with readFileSync
and then passes the buffer to AI.transcribe; instead, stop using readFileSync
(and avoid buffering the file) and pass the memo file path directly to
AI.transcribe (e.g., use the temporary/copied file path produced by
copyFileSync) so the transcription uses streamed/file-based input; update all
occurrences (including the similar block around where readFileSync is used
again) to pass the file path instead of a Buffer and remove the now-unnecessary
synchronous read and Buffer handling.
- Line 2: Update the path handling to produce an absolute, normalized export
destination by importing and using resolve from node:path alongside the existing
basename and join imports (replace or augment the current import { basename,
join } with { basename, join, resolve }). Locate the code that builds the export
destination using join (the function/logic that constructs the export path for
voice memos) and wrap or replace it with resolve (e.g., resolve(join(...)) or
resolve(...)) so the resulting path is absolute and normalized before any file
operations or export returns. Ensure any exported path values and tests that
consume them now rely on this resolved absolute path.
- Around line 44-59: Add multi-destination output flags to both CLI subcommands
and wire them into the existing action handlers: add options -o/--output <file>
and -c/--clipboard (or --clipboard) to vm.command("transcribe") and
vm.command("search"), extend the TranscribeOpts/use a shared opts type so
transcribeAction(id, opts) and searchAction(query, opts) receive the output
targets, and inside those functions use the existing clipboard utility
(src/utils/clipboard.ts) and file write logic to emit results to the chosen
destination(s) rather than only stdout; ensure flag names match other commands
(e.g. -o/--output and -c/--clipboard) and that the action calls still use
handleErrors(() => transcribeAction(...)) / handleErrors(() =>
searchAction(...)).

In `@src/timer/index.ts`:
- Around line 185-192: Replace the node:child_process spawn call (nodeSpawn
creating the variable child) with Bun.spawn to follow project conventions: call
Bun.spawn with the same argv (scriptPath, "__bg-run__", String(durationMs),
title, id, String(notify), String(say), String(repeat)) and appropriate stdio
options, then call .unref() on the returned process to detach it (remove the
detached:true usage). Update any references to child accordingly and keep the
spawn invocation near the existing Bun.spawn usage pattern already present in
this file.

In `@src/utils/ai/providers/AILocalProvider.ts`:
- Around line 48-67: The custom language detector created in AILocalProvider
(when options?.languageDetection is truthy) may not be disposed if
detector.detectFromAudio(audioData) throws; wrap the detectFromAudio call in a
try/finally (or try/catch/finally) so that detector.dispose() is always called
for the locally created detector, rethrowing the error after disposal; ensure
you only dispose detectors created for one-off use (i.e., when
options?.languageDetection was used) and do not dispose the shared
this.langDetector.

In `@src/utils/audio/converter.ts`:
- Around line 187-189: The code assumes 32-bit samples are Float32 and creates a
Float32Array view directly from pcm which can throw if pcm.byteOffset is not
4‑byte aligned; update the logic that handles bitsPerSample === 32 (in the
function that reads PCM buffers where variables bitsPerSample and pcm are used)
to first read the WAV audioFormat/format code (the 2‑byte value at byte offset
20) and branch: if format code indicates IEEE float (3) create a Float32Array,
otherwise if PCM (1) create an Int32Array and convert/normalize to float
samples; to avoid alignment errors always copy the sample bytes into a freshly
allocated ArrayBuffer (or use pcm.slice/copy) before constructing the typed
array so the byteOffset is zero/aligned and the RangeError cannot occur.

In `@src/youtube/commands/transcribe.test.ts`:
- Around line 36-56: Add a regression test to cover millisecond carry-over when
rounding causes a second boundary overflow: add an it case for formatTimestamp
that passes 59.9995 and asserts the result is "00:01:00.000" (verifying seconds
and minutes carry correctly); place the new test alongside the other
formatTimestamp cases so it exercises the rounding/overflow logic in
formatTimestamp.

In `@src/youtube/commands/transcribe.ts`:
- Line 207: Change transcribeAction to accept a single object parameter (e.g.,
type TranscribeParams = { urlArg?: string; options: TranscribeOptions; cmd:
Command }) instead of three separate parameters; update the function signature
async function transcribeAction(params: TranscribeParams): Promise<void> and
destructure { urlArg, options, cmd } inside it, and then update all call sites
that currently call transcribeAction(urlArg, options, cmd) to pass an object
transcribeAction({ urlArg, options, cmd }); also update any exported types or
JSDoc accordingly so callers and tests use the new object form.
- Around line 35-42: The extractVideoId function currently uses regexes that can
match non-YouTube hosts and miss watch URLs where v isn't the first query param;
update it to parse the input with the URL constructor (fall back to treating
input as a raw ID if URL parsing fails), then deterministically check hostname
(allow exact hosts and subdomains that endWith 'youtube.com' or equal
'youtu.be'), extract the ID from the 'v' query parameter for watch URLs, from
the pathname for /embed/, /shorts/, and youtu.be short links, and only if none
of those apply validate the input against the 11-char ID pattern before
returning; keep all logic in extractVideoId to ensure robust, host-verified
extraction.
- Around line 370-381: The current construction of CaptionResult passes an empty
segments array when transcription.segments is missing, which yields empty
.srt/.vtt output; update the block that builds CaptionResult (around the
transcription, CaptionResult, and formatOutput usage) to detect when the
requested format is a subtitle format (e.g., 'srt' or 'vtt') and
transcription.segments is undefined or empty, and in that case either throw a
clear error or synthesize a single cue: create one segment with text =
transcription.text, start = 0, and end = transcription.duration (or a sensible
default like 5 seconds if duration is unavailable), then pass that segments
array into formatOutput so subtitle outputs contain a single usable cue instead
of being empty.

---

Duplicate comments:
In `@src/macos/commands/voice-memos/index.ts`:
- Around line 186-189: transcribeAction's batch branch calls transcribeAll
without performing the same AI fallback or honoring --force; update
transcribeAll (or its caller) so batch transcription uses the same logic as
transcribeOne: accept a force/fallback flag and, for each memo, attempt embedded
transcript first and if missing or force=true then invoke the AI fallback path
(or call transcribeOne per memo) so --force re-triggers AI transcription and
--all behaves consistently with single transcribe behavior.

In `@src/timer/index.ts`:
- Around line 383-397: The interactive duration validator inside the
withCancel(p.text(... validate(value) { ... })) currently only checks for ms > 0
and can accept values above MAX_TIMEOUT_MS; update the validator to parse the
duration, then reject values that exceed MAX_TIMEOUT_MS (use the MAX_TIMEOUT_MS
constant) by returning a validation message like "Duration exceeds maximum
allowed X (e.g. 24h)"; ensure the validator only accepts values where 0 < ms <=
MAX_TIMEOUT_MS so the rest of the flow (background mode / setTimeout) never
receives an overflow-prone value.

In `@src/utils/terminal.ts`:
- Around line 1-5: Remove the verbose JSDoc that duplicates the code logic and
replace it with a concise note that documents only the non-obvious rationale:
mention that CMUX_BUNDLE_ID is checked first because cmux clears TERM_PROGRAM,
and otherwise TERM_PROGRAM is used; keep a short one-line summary like "Detect
terminal name, preferring CMUX_BUNDLE_ID over TERM_PROGRAM for cmux support."
Update the JSDoc located above the terminal detection function to this trimmed
form and ensure the existing test-backed mapping logic and env var names
(CMUX_BUNDLE_ID, TERM_PROGRAM) remain referenced only where necessary.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5faffd7f-cc04-4571-aa0f-60447f16af62

📥 Commits

Reviewing files that changed from the base of the PR and between 261a829 and 0745210.

📒 Files selected for processing (12)
  • src/ai/index.ts
  • src/github/commands/issue.ts
  • src/github/commands/pr.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/audio/converter.test.ts
  • src/utils/audio/converter.ts
  • src/utils/terminal.ts
  • src/youtube/commands/transcribe.test.ts
  • src/youtube/commands/transcribe.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Do not add file-path comments as the first line of files
Do not add obvious comments that restate what the code already says
Never use one-line if statements; always use block form with braces
Include an empty line before if statements, unless the preceding line is a variable declaration used by that if
Include an empty line after closing } unless followed by else, catch, finally, or another }
Use object parameters for functions with 3+ parameters or optional parameters
Use positional parameters only for 1-2 required and obvious parameters such as estimateTokens(text) or resolve(base, path)
Prefer error: err over error: err instanceof Error ? err.message : String(err) when the error field accepts unknown

Files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Do not use as any for type casting; use proper type narrowing, type guards, or explicit interfaces instead
Use discriminant checks for union types (e.g., entity.className === "User")

Files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
src/utils/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place general-purpose utility functions in src/utils/ instead of inside tool directories

Files:

  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/utils/audio/converter.ts
  • src/utils/ai/providers/AILocalProvider.ts
src/**/index.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/index.{ts,tsx}: Use commander for parsing command-line arguments with subcommands and options
Use @clack/prompts for interactive user experience in new tools; use @inquirer/prompts only for legacy tools
Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations
Use chalk for colored terminal output but strip ANSI codes for non-TTY environments
Use Bun.spawn() for executing external commands and handle stdout/stderr streams properly
Use Node.js path module for cross-platform path handling and resolve relative paths to absolute
Use Bun's native file APIs (Bun.write()) for better performance in file operations

Files:

  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/ai/index.ts
🧠 Learnings (35)
📓 Common learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:14:03.722Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), there is no CI formatter (e.g., Prettier/Biome) configured or enforced. Do not flag formatting mismatches based on static analysis tool annotations (e.g., GitHub Actions CI formatter warnings) in this repository, as they are not representative of actual enforced formatting rules. TypeScript files under src/ should be reviewed for logical correctness and consistency with existing code patterns instead.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:31.715Z
Learning: In genesiscz/GenesisTools (src/**/*.ts), do not add inline comments for well-known POSIX patterns or standard idioms (e.g., process.kill(pid, 0) probe) when the surrounding code is already self-documenting via descriptive function/variable names. The project enforces a no-obvious-comments rule: avoid comments that merely restate what the code clearly expresses.
📚 Learning: 2026-02-20T00:52:27.023Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 31
File: src/ask/utils/helpers.ts:3-3
Timestamp: 2026-02-20T00:52:27.023Z
Learning: In all TypeScript source files under src, prefer using picocolors for colored terminal output in new code. Picocolors is smaller and faster than chalk, so adopt it for CLI output coloring and avoid adding chalk in new code paths unless there is a compelling compatibility reason.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-17T01:30:56.939Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:130-139
Timestamp: 2026-03-17T01:30:56.939Z
Learning: In genesiscz/GenesisTools, do not suggest converting two-argument functions with an optional second parameter (for example setMute(muted: boolean, app?: string)) to an object-parameter form. The project prefers simple positional parameters for short utility functions, even when an optional argument is present. The object-parameter guideline should only apply when a function has 3 or more parameters.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/youtube/commands/transcribe.test.ts
  • src/utils/audio/converter.test.ts
  • src/utils/terminal.ts
  • src/github/types.ts
  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
  • src/utils/audio/converter.ts
  • src/youtube/commands/transcribe.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/github/commands/pr.ts
  • src/ai/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `chalk` for colored terminal output but strip ANSI codes for non-TTY environments

Applied to files:

  • src/utils/terminal.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Do not add obvious comments that restate what the code already says

Applied to files:

  • src/utils/terminal.ts
  • src/timer/index.ts
📚 Learning: 2026-02-24T15:39:14.492Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:182-210
Timestamp: 2026-02-24T15:39:14.492Z
Learning: Do not suggest replacing chalk with picocolors in src/github/* files. For the GenesisTools repository, chalk is the established standard for colored terminal output in the src/github toolset, and reviews should preserve this dependency across all TypeScript files under src/github (e.g., src/github/lib/*.ts, src/github/*.ts). If a review topic concerns color output, prefer chalk-specific patterns or compatibility guidance rather than introducing an alternative library.

Applied to files:

  • src/github/types.ts
  • src/github/commands/issue.ts
  • src/github/commands/pr.ts
📚 Learning: 2026-02-24T15:39:14.492Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:182-210
Timestamp: 2026-02-24T15:39:14.492Z
Learning: In src/github/, the review output architecture should route to separate formatters: formatReviewTerminal() for colored terminal output, formatReviewMarkdown() for markdown, and formatReviewJSON() for JSON. The caller should select the appropriate formatter based on output mode flags, and avoid adding per-formatter TTY detection since the architecture handles terminal vs non-terminal formatting at the formatter orchestration level. This guideline applies to all TypeScript files under src/github that implement or reference the review output formatting logic.

Applied to files:

  • src/github/types.ts
  • src/github/commands/issue.ts
  • src/github/commands/pr.ts
📚 Learning: 2026-03-08T18:38:36.013Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 80
File: src/github/commands/review.ts:290-306
Timestamp: 2026-03-08T18:38:36.013Z
Learning: In the GenesisTools repo, review sessions in src/github/lib/review-session.ts and src/github/commands/review.ts use a short-lived, read-only cache with a 7-day TTL. Sessions are not updated after reply/resolve mutations; users should re-fetch with 'tools github review <pr> --llm' to obtain fresh data from the GitHub API. Do not treat session staleness after mutations as a bug; this is an intentional design choice to keep the CLI simple. Applies to TypeScript files under src/github/.

Applied to files:

  • src/github/types.ts
  • src/github/commands/issue.ts
  • src/github/commands/pr.ts
📚 Learning: 2026-03-17T01:32:56.919Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:122-127
Timestamp: 2026-03-17T01:32:56.919Z
Learning: In genesiscz/GenesisTools, mute/config state (SayConfig, isMuted, setMute, getConfig, getConfigForRead) belongs at the CLI layer (src/say/) rather than in the TTS utility layer (src/utils/macos/tts.ts). The tts.ts file should remain a pure macOS TTS primitive (getVoiceMap, speak, listVoices, listVoicesStructured) without any application-level config or mute logic. The `app` field in SpeakOptions should also be removed since it only exists to support per-app muting.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `commander` for parsing command-line arguments with subcommands and options

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/ai/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `clack/prompts` for interactive user experience in new tools; use `inquirer/prompts` only for legacy tools

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/ai/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use positional parameters only for 1-2 required and obvious parameters such as `estimateTokens(text)` or `resolve(base, path)`

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/timer/index.ts
📚 Learning: 2026-03-15T15:54:08.510Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 105
File: src/ask/index.ts:66-74
Timestamp: 2026-03-15T15:54:08.510Z
Learning: In genesiscz/GenesisTools, manual argv._[0] dispatch in src/ask/index.ts (and similar entry-point files) is intentional for lazy loading: subcommand modules (e.g. ask/commands/configure) are imported via dynamic import() inside the dispatch branch rather than registered as Commander subcommands. This avoids eagerly loading all subcommand modules at startup. Do NOT flag this pattern as a violation of the "use Commander for subcommands" guideline; the lazy-loading benefit outweighs the missing Commander help/error-handling for these entry points.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/ai/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `Bun.spawn()` for executing external commands and handle stdout/stderr streams properly

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use object parameters for functions with 3+ parameters or optional parameters

Applied to files:

  • src/timer/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T16:03:57.231Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:262-303
Timestamp: 2026-03-15T16:03:57.231Z
Learning: In `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`, the `durations` array in `calculateRunCost()` and `WorkflowSummary` intentionally stores one entry per job (job-level duration in seconds), not per-run wall-clock time. This matches GitHub's billing model (rounded up per job). The aggregated/top-N output represents total billable job time, not wall-clock run time. Do not flag this as an issue in future reviews.

Applied to files:

  • src/timer/index.ts
  • src/youtube/commands/transcribe.ts
📚 Learning: 2026-03-15T16:03:33.870Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:59-67
Timestamp: 2026-03-15T16:03:33.870Z
Learning: In `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`, ubuntu-slim runner detection (1-core, $0.002/min) is intentionally omitted from `RUNNER_RATES` and `detectRunnerType`. The maintainer considers it rarely used in practice and has explicitly decided not to add detection for it. Do not flag its absence as an issue in future reviews.

Applied to files:

  • src/timer/index.ts
📚 Learning: 2026-02-20T01:44:48.367Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 51
File: src/telegram/commands/history.ts:0-0
Timestamp: 2026-02-20T01:44:48.367Z
Learning: In `src/telegram/commands/history.ts`, the FloodWait error handling intentionally exits the download loop and saves `highestId` for incremental sync. Re-running the command resumes from the saved ID. This design is necessary because the GramJS async iterator cannot be resumed after an exception—a retry loop would restart from scratch rather than continuing.

Applied to files:

  • src/youtube/commands/transcribe.ts
📚 Learning: 2026-03-17T01:47:58.691Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:148-148
Timestamp: 2026-03-17T01:47:58.691Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation plan documents (design/intent descriptions), not production code. Do not raise code-quality, robustness, or implementation-detail review comments (e.g., race conditions, error handling, temp file strategy) on these plan files. Such concerns are addressed in the actual implementation files (e.g., src/**/*.ts).

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:47:59.002Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T14:37:47.990Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 87
File: docs/typescript-sdks/apple/github-repos-index.md:94-94
Timestamp: 2026-03-11T14:37:47.990Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), docs under `docs/typescript-sdks/apple/` (e.g., `github-repos-index.md`, `macos-node-api.md`) are auto-generated. Do not flag trivial markdown formatting issues (e.g., MD022 blank lines around headings, MD031, etc.) in these files, as they will not be manually fixed.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T17:12:27.285Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/clarity/ui/src/routes/settings.tsx:1-1
Timestamp: 2026-03-11T17:12:27.285Z
Learning: In genesiscz/GenesisTools, `SafeJSON` in `src/utils/json.ts` intentionally uses `comment-json.parse` for parsing (to handle `//` comments, trailing commas, unquoted keys) but `JSON.stringify` (native) for stringification. Do not flag `SafeJSON.stringify` usages as unnecessary — the intent is a unified drop-in for `JSON` that parses leniently but serialises as standard JSON.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-09T13:39:43.059Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:48:08.317Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:209-222
Timestamp: 2026-03-17T01:48:08.317Z
Learning: In genesiscz/GenesisTools, files under .claude/plans/ are implementation plan documents, not production code. Do not raise code-quality, consistency, or implementation issues (e.g., naming conflicts, missing migrations, architectural concerns) against these plan files. Issues in plan documents are expected to be addressed during actual implementation. Only flag genuinely problematic structural issues (e.g., broken links, missing required sections) if they would block implementation. This extends the existing .claude/ directory exemption beyond markdownlint to all review categories.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Prefer `error: err` over `error: err instanceof Error ? err.message : String(err)` when the error field accepts unknown

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
🧬 Code graph analysis (7)
src/youtube/commands/transcribe.test.ts (1)
src/youtube/commands/transcribe.ts (2)
  • extractVideoId (35-52)
  • CaptionSegment (23-27)
src/utils/audio/converter.test.ts (1)
src/utils/audio/converter.ts (2)
  • toFloat32Audio (11-23)
  • convertToWhisperWav (29-45)
src/macos/commands/voice-memos/index.ts (2)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/ai/index.ts (1)
  • AI (27-58)
src/timer/index.ts (3)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/format.ts (1)
  • parseDuration (123-159)
src/github/commands/issue.ts (2)
src/github/lib/output.ts (1)
  • formatIssue (38-47)
src/utils/github/utils.ts (1)
  • verbose (40-44)
src/utils/ai/providers/AILocalProvider.ts (3)
src/utils/ai/types.ts (11)
  • AITask (2-2)
  • AITranscriptionProvider (71-73)
  • AITranslationProvider (75-77)
  • AISummarizationProvider (79-81)
  • TranscribeOptions (31-40)
  • TranscriptionResult (42-47)
  • TranslateOptions (49-53)
  • TranslationResult (55-59)
  • SummarizeOptions (61-64)
  • SummarizationResult (66-69)
  • OnProgress (21-21)
src/utils/ai/LanguageDetector.ts (2)
  • LanguageDetector (33-86)
  • createLanguageDetector (317-353)
src/utils/audio/converter.ts (1)
  • toFloat32Audio (11-23)
src/ai/index.ts (5)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/ai/index.ts (1)
  • AI (27-58)
src/utils/clipboard.ts (2)
  • copyToClipboard (4-14)
  • readFromClipboard (16-18)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/ai/types.ts (1)
  • AITask (2-2)
🔇 Additional comments (18)
src/utils/terminal.ts (1)

7-30: Terminal detection precedence and fallback logic look solid.

CMUX priority, known terminal mappings, and unknown/empty fallback handling are all coherent and user-friendly.

src/utils/ai/providers/AILocalProvider.ts (2)

147-154: All pipeline load failures collapsed into "unsupported language pair" error.

Network failures, cache corruption, or runtime errors will produce a misleading "Model not found" message, making root-cause diagnosis difficult.

🔧 Proposed fix
         try {
             pipe = await this.getPipeline("translation", model);
-        } catch {
+        } catch (err) {
+            const cause = err instanceof Error ? err.message : String(err);
             throw new Error(
-                `Unsupported language pair: ${from} → ${to}. ` +
-                    `Model "${model}" not found. Try an explicit --model option or pivot through English.`
+                `Failed to load translation model "${model}" for ${from ?? "auto"} → ${to}. ` +
+                    `Try an explicit --model option or pivot through English. Cause: ${cause}`
             );
         }

180-180: getPipeline has 3 parameters but uses positional args.

Per coding guidelines, functions with 3+ parameters should use object parameters.

♻️ Proposed refactor
-    private async getPipeline(task: PipelineType, model: string, onProgress?: OnProgress): Promise<PipelineInstance> {
+    private async getPipeline(params: {
+        task: PipelineType;
+        model: string;
+        onProgress?: OnProgress;
+    }): Promise<PipelineInstance> {
+        const { task, model, onProgress } = params;
         const key = `${task}:${model}`;

Update call sites accordingly:

-        const pipe = await this.getPipeline("automatic-speech-recognition", model, onProgress);
+        const pipe = await this.getPipeline({ task: "automatic-speech-recognition", model, onProgress });

As per coding guidelines: "Use object parameters for functions with 3+ parameters or optional parameters".

src/ai/index.ts (5)

115-119: summarize --provider option is unused.

SummarizeFlags.provider is declared but never passed to AI.summarize() at line 156, so the CLI option has no effect.

🔧 Proposed fix
-        const result = await AI.summarize(input, { maxLength });
+        const summarizer = await AI.Summarizer.create({ provider: opts.provider });
+        try {
+            const result = await summarizer.summarize(input, { maxLength });
+            // ... rest of success handling
+        } finally {
+            summarizer.dispose();
+        }

Alternatively, if the convenience method should support provider selection, update the AI.summarize signature.

Also applies to: 150-156


235-238: classify --provider option is unused.

ClassifyFlags.provider is declared but cmdClassify always calls classifyText() (macOS NLP), ignoring the provider option entirely.

🔧 Proposed fix

Either wire the provider option to dispatch to different classification backends, or remove the --provider option from the CLI definition (lines 641) to avoid misleading users.

-    .option("--provider <provider>", "AI provider")

Also applies to: 264-268


533-541: Interactive summarize now cleans up temp file properly.

The try/finally block ensures the temp file is removed even on error.


373-377: Template literal fix looks good.

HF token masking now correctly uses nested template literals.


1-19: CLI structure follows project conventions.

Uses Commander for subcommands, @clack/prompts for interactive mode, picocolors for colored output, and proper path handling via node:path. The entry point correctly wraps in try/catch and exits with appropriate codes.

Also applies to: 594-687

src/utils/audio/converter.ts (4)

1-3: LGTM!

Clean imports from Node.js standard library.


11-45: LGTM!

The temp file lifecycle is properly managed: tmpIn is cleaned in the finally block when created from a buffer, and tmpOut is cleaned inside convertFileToWhisperWav on both success and error paths.


47-69: LGTM!

The cleanup logic correctly handles all exit paths: outputPath is cleaned inside tryAfconvert/tryFfmpeg on success, and the catch block ensures cleanup on any error before rethrowing.


201-211: LGTM!

Simple and correct utility functions.

src/utils/audio/converter.test.ts (3)

5-51: LGTM!

Thorough test: generates a synthetic WAV with known properties, validates output length, value range, and RMS amplitude to confirm audible content.


53-88: LGTM!

Good coverage of non-standard WAV structure with extra chunks before data. This exercises the chunk-scanning logic in parseWavToFloat32.


90-103: LGTM!

The threw flag pattern ensures the test fails if convertToWhisperWav unexpectedly accepts garbage input. This addresses the previous review feedback.

src/youtube/commands/transcribe.test.ts (1)

4-34: Good utility coverage for URL extraction variants.

These cases provide solid baseline coverage for supported URL schemas and invalid inputs.

src/macos/commands/voice-memos/index.ts (1)

31-31: Good fix: argument parsing now uses explicit base-10 radix.

This avoids ambiguous numeric parsing behavior in CLI inputs.

Also applies to: 38-38, 46-46

src/github/types.ts (1)

209-209: LGTM.

The shared save field matches the renamed CLI surface for both issue and PR commands.

@genesiscz
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@genesiscz
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (7)
src/utils/ai/providers/AILocalProvider.ts (3)

157-164: ⚠️ Potential issue | 🟠 Major

Don't collapse all load failures into "unsupported language pair."

This catch block rewrites unrelated failures (network/cache/runtime) into a misleading translation-pair error, which makes diagnosis harder.

Proposed fix
-        } catch {
-            throw new Error(
-                `Unsupported language pair: ${from} → ${to}. ` +
-                    `Model "${model}" not found. Try an explicit --model option or pivot through English.`
-            );
+        } catch (err) {
+            const cause = err instanceof Error ? err.message : String(err);
+            throw new Error(
+                `Failed to load translation model "${model}" for ${from ?? "auto"} → ${to}. ` +
+                    `Try an explicit --model option or pivot through English. Cause: ${cause}`
+            );
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/ai/providers/AILocalProvider.ts` around lines 157 - 164, The catch
in AILocalProvider around getPipeline("translation", model) hides all errors as
an "Unsupported language pair" message; update the try/catch in the method
containing getPipeline so you capture the caught error (e.g. catch (err)) and
only translate it into the current Unsupported language-pair Error when the
failure is actually due to a missing model (detect via error type/message or a
specific NotFound/model-not-found sentinel); for any other error (network,
cache, runtime) rethrow the original error (or wrap it while preserving err) so
callers get the real failure information instead of a misleading
translation-pair message.

49-68: ⚠️ Potential issue | 🟡 Minor

Resource leak: custom language detector not disposed on error.

If detector.detectFromAudio(audioData) throws at line 63, the custom detector created at line 55 won't be disposed, leaking the underlying pipeline resources.

🛡️ Proposed fix
         if (options?.languageDetection) {
             detector = createLanguageDetector(options.languageDetection);
         } else {
             if (!this.langDetector) {
                 this.langDetector = createLanguageDetector();
             }
 
             detector = this.langDetector;
         }
-        const detected = await detector.detectFromAudio(audioData);
-        language = detected.language;
 
-        if (options?.languageDetection) {
-            detector.dispose();
+        try {
+            const detected = await detector.detectFromAudio(audioData);
+            language = detected.language;
+        } finally {
+            if (options?.languageDetection) {
+                detector.dispose();
+            }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/ai/providers/AILocalProvider.ts` around lines 49 - 68, The custom
language detector created via createLanguageDetector(options.languageDetection)
may not be disposed if detector.detectFromAudio(audioData) throws; wrap the
detection call in a try/finally and ensure disposal of the locally-created
detector (only when options?.languageDetection is truthy) in the finally block,
rethrowing the error after cleanup. In AILocalProvider, track whether the
detector is the temporary one (e.g., localDetector =
Boolean(options?.languageDetection)), call await detector.detectFromAudio(...)
inside try, and in finally call detector.dispose() only when localDetector is
true so this.langDetector remains intact.

190-190: 🛠️ Refactor suggestion | 🟠 Major

Refactor getPipeline to object params.

getPipeline(task, model, onProgress?) uses 3 parameters; this should be an object parameter shape to align with repo conventions and reduce call-site ordering risk.

Proposed refactor
-    private async getPipeline(task: PipelineType, model: string, onProgress?: OnProgress): Promise<PipelineInstance> {
-        const key = `${task}:${model}`;
+    private async getPipeline(params: {
+        task: PipelineType;
+        model: string;
+        onProgress?: OnProgress;
+    }): Promise<PipelineInstance> {
+        const { task, model, onProgress } = params;
+        const key = `${task}:${model}`;

Then update all call sites:

-        const pipe = await this.getPipeline("automatic-speech-recognition", model, onProgress);
+        const pipe = await this.getPipeline({ task: "automatic-speech-recognition", model, onProgress });

-            pipe = await this.getPipeline("translation", model);
+            pipe = await this.getPipeline({ task: "translation", model });

-        const pipe = await this.getPipeline("summarization", model);
+        const pipe = await this.getPipeline({ task: "summarization", model });

As per coding guidelines: "Use object parameters for functions with 3+ parameters or optional parameters".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/ai/providers/AILocalProvider.ts` at line 190, Refactor the private
method getPipeline to accept a single options object instead of positional args:
replace signature getPipeline(task: PipelineType, model: string, onProgress?:
OnProgress): Promise<PipelineInstance> with an object parameter (e.g., { task,
model, onProgress }) and update the implementation to destructure that object;
then update every call site to pass an object with those named properties so
order no longer matters and optional onProgress is explicit. Ensure types are
updated (use an interface/type for the options), retain the return type
Promise<PipelineInstance>, and update any references to the parameter names
inside getPipeline accordingly.
src/macos/commands/voice-memos/index.ts (2)

173-187: ⚠️ Potential issue | 🟡 Minor

Resolve dest to absolute path for consistency.

The transcribeOne function resolves opts.output to an absolute path (line 290), but exportAction uses dest directly. For consistent and deterministic behavior regardless of working directory, resolve the path at the start.

Proposed fix
 function exportAction(id: number, dest: string): void {
     const memo = resolveMemo(id);
+    const destDir = resolve(dest);

-    if (!existsSync(dest)) {
-        mkdirSync(dest, { recursive: true });
+    if (!existsSync(destDir)) {
+        mkdirSync(destDir, { recursive: true });
     }

     const datePrefix = memo.date.toISOString().slice(0, 10);
     const safeTitle = memo.title.replace(/[/\\?%*:|"<>]/g, "-");
     const ext = basename(memo.path).includes(".") ? `.${basename(memo.path).split(".").pop()}` : ".m4a";
-    const destFile = join(dest, `${datePrefix}-${safeTitle}${ext}`);
+    const destFile = join(destDir, `${datePrefix}-${safeTitle}${ext}`);

As per coding guidelines: "Use Node.js path module for cross-platform path handling and resolve relative paths to absolute".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/macos/commands/voice-memos/index.ts` around lines 173 - 187, The
exportAction function uses the incoming dest path directly which can be
relative; resolve it to an absolute path at the start (e.g., call
path.resolve(dest)) and then use that resolved value for mkdirSync, join, and
the final destination file; update references to dest within exportAction
(function name exportAction, local vars destFile, datePrefix, safeTitle) so all
IO uses the resolved absolute path to ensure consistent cross-platform behavior.

63-68: 🧹 Nitpick | 🔵 Trivial

Add output destination options to the search command.

The transcribe command now supports -o/--output and -c/--clipboard, but the search command still only outputs to stdout. For consistency with other CLI tools in the repo and per coding guidelines, add the same output destination options.

Proposed fix
     vm.command("search")
         .description("Search memos by title and transcript text")
         .argument("<query>", "Search query")
-        .action(async (query: string) => {
-            await handleErrors(() => searchAction(query));
+        .option("-o, --output <path>", "Write output to file")
+        .option("-c, --clipboard", "Copy output to clipboard")
+        .action(async (query: string, opts: { output?: string; clipboard?: boolean }) => {
+            await handleErrors(() => searchAction(query, opts));
         });

Then update searchAction to accept and handle the options similarly to transcribeOne.

As per coding guidelines: "Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/macos/commands/voice-memos/index.ts` around lines 63 - 68, Update the
vm.command("search") invocation to accept the same output options as transcribe
(add .option("-o, --output <path>", ...) and .option("-c, --clipboard", ...))
and change its .action to pass the parsed options into searchAction (instead of
only query) so searchAction(query, options) can decide the destination; then
modify the searchAction function to accept an options parameter and implement
writing results to a file when options.output is set, copying to clipboard via
clipboardy when options.clipboard is true, and falling back to stdout otherwise,
mirroring the behavior used by transcribeOne.
src/utils/ai/transcription-format.ts (1)

24-27: ⚠️ Potential issue | 🟠 Major

Don't return untimed text for subtitle formats.

When result.segments is missing, toSRT() and toVTT() still emit plain text. That produces malformed subtitle files for callers requesting srt/vtt. Synthesize a single cue from result.duration or throw so the caller can fall back to "text".

Also applies to: 39-41

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/ai/transcription-format.ts` around lines 24 - 27, The toSRT and
toVTT functions currently return plain text when result.segments is missing;
update them to not emit untimed text for subtitle formats by either synthesizing
a single timed cue using result.duration (use result.duration as the cue end
time and 0 as start) or throw an error so callers can fall back to the "text"
format; locate the logic in toSRT and toVTT that checks result.segments and
replace the early return of result.text with code that builds a single cue from
result.duration (or throws if duration is absent) while preserving
TranscriptionResult handling.
src/transcribe/index.ts (1)

89-93: ⚠️ Potential issue | 🟠 Major

Reject unknown --provider and --format values before dispatch.

These options accept arbitrary runtime strings. --format foo makes formatOutput() fall through and return undefined, and --provider nope is blindly cast at Line 92, so failures surface deep in the transcription path instead of as a clear CLI error. Validate both against the allowed sets in the action handler before calling runTranscription().

Also applies to: 271-285

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/transcribe/index.ts` around lines 89 - 93, Validate and reject unknown
CLI values for provider and format before dispatch: in the action handler that
calls runTranscription(), check opts.provider against the allowed AIProviderType
set (e.g., the same enum/union used throughout) and opts.format against the
allowed formats used by formatOutput(), and throw or exit with a clear CLI error
if either is invalid; update the code paths that currently set provider =
opts.local ? "local-hf" : (opts.provider as AIProviderType | undefined) and
format = opts.format ?? "text" to perform explicit validation and normalization
before calling runTranscription() so invalid strings are caught early.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/e2e/voice-memos.e2e.test.ts`:
- Around line 91-94: The test "transcribe --all exits 0" can be flaky because
runTool uses a 15s default timeout and transcribing multiple memos may exceed
it; update the test to call runTool with an extended timeout (e.g., 60s) or use
a test-only flag to limit/mode-mock transcription instead of the real bulk
operation: locate the test case referencing runTool(["macos", "voice-memos",
"transcribe", "--all"]) and either pass a longer timeout option to runTool or
change the invocation to a mocked/limited transcription mode so the test
reliably finishes within CI time.
- Around line 56-64: In the "list" test inside describe("list") / it("list exits
0 and shows table headers"), remove the unnecessary intermediate variable by
replacing const output = r.stdout with using r.stdout directly in the two
assertions; keep the call to runTool(["macos", "voice-memos", "list"]) and the
exitCode assertion, then change expect(output).toContain(...) to
expect(r.stdout).toContain(...) for both header checks (references: runTool, r,
r.stdout, the "list" test).
- Around line 73-81: The test hardcodes memo ID 377 making it
environment-dependent; modify the "export" test (describe/it) to first import
and call listMemos() (or the module function that returns memos) to obtain a
real memo id, and if listMemos() returns empty, skip the test or early-return
with a warning; otherwise use the first memo's id when calling
runTool(["macos","voice-memos","export", memoId, EXPORT_DIR]) and keep the
existing EXPORT_DIR assertions (existsSync, readdirSync) so the test works
reliably across environments.

In `@src/macos/commands/voice-memos/index.ts`:
- Around line 261-264: The transcriber creation currently casts opts.provider
directly to AIProviderType which allows invalid CLI values; before calling
AI.Transcriber.create (and in transcribeAction/transcribeOne code paths),
validate opts.provider against the allowed AIProviderType values (or a provider
lookup map) and only pass a typed value when it matches; if the provided string
is invalid, surface a clear user error (or fallback to undefined/default)
instead of blind casting so AI.Transcriber.create receives a valid provider
type.

In `@src/transcribe/index.ts`:
- Around line 38-46: The interactiveMode() selection for destination
(Terminal/Clipboard/File) isn't being propagated to runTranscription(), so
TranscribeFlags only carries output/clipboard and runTranscription() still
prints to stdout even when Clipboard or File was selected; update the
TranscribeFlags interface to include an explicit destination (e.g., stdout or
destination enum), change interactiveMode() to set that flag, and modify
runTranscription() signature and callers to accept the new destination/stdout
flag; finally, gate any console.log() output in runTranscription() behind the
destination/stdout check (only log when destination==Terminal/stdout) so
choosing Clipboard or File does not print the transcript to stdout.

---

Duplicate comments:
In `@src/macos/commands/voice-memos/index.ts`:
- Around line 173-187: The exportAction function uses the incoming dest path
directly which can be relative; resolve it to an absolute path at the start
(e.g., call path.resolve(dest)) and then use that resolved value for mkdirSync,
join, and the final destination file; update references to dest within
exportAction (function name exportAction, local vars destFile, datePrefix,
safeTitle) so all IO uses the resolved absolute path to ensure consistent
cross-platform behavior.
- Around line 63-68: Update the vm.command("search") invocation to accept the
same output options as transcribe (add .option("-o, --output <path>", ...) and
.option("-c, --clipboard", ...)) and change its .action to pass the parsed
options into searchAction (instead of only query) so searchAction(query,
options) can decide the destination; then modify the searchAction function to
accept an options parameter and implement writing results to a file when
options.output is set, copying to clipboard via clipboardy when
options.clipboard is true, and falling back to stdout otherwise, mirroring the
behavior used by transcribeOne.

In `@src/transcribe/index.ts`:
- Around line 89-93: Validate and reject unknown CLI values for provider and
format before dispatch: in the action handler that calls runTranscription(),
check opts.provider against the allowed AIProviderType set (e.g., the same
enum/union used throughout) and opts.format against the allowed formats used by
formatOutput(), and throw or exit with a clear CLI error if either is invalid;
update the code paths that currently set provider = opts.local ? "local-hf" :
(opts.provider as AIProviderType | undefined) and format = opts.format ?? "text"
to perform explicit validation and normalization before calling
runTranscription() so invalid strings are caught early.

In `@src/utils/ai/providers/AILocalProvider.ts`:
- Around line 157-164: The catch in AILocalProvider around
getPipeline("translation", model) hides all errors as an "Unsupported language
pair" message; update the try/catch in the method containing getPipeline so you
capture the caught error (e.g. catch (err)) and only translate it into the
current Unsupported language-pair Error when the failure is actually due to a
missing model (detect via error type/message or a specific
NotFound/model-not-found sentinel); for any other error (network, cache,
runtime) rethrow the original error (or wrap it while preserving err) so callers
get the real failure information instead of a misleading translation-pair
message.
- Around line 49-68: The custom language detector created via
createLanguageDetector(options.languageDetection) may not be disposed if
detector.detectFromAudio(audioData) throws; wrap the detection call in a
try/finally and ensure disposal of the locally-created detector (only when
options?.languageDetection is truthy) in the finally block, rethrowing the error
after cleanup. In AILocalProvider, track whether the detector is the temporary
one (e.g., localDetector = Boolean(options?.languageDetection)), call await
detector.detectFromAudio(...) inside try, and in finally call detector.dispose()
only when localDetector is true so this.langDetector remains intact.
- Line 190: Refactor the private method getPipeline to accept a single options
object instead of positional args: replace signature getPipeline(task:
PipelineType, model: string, onProgress?: OnProgress): Promise<PipelineInstance>
with an object parameter (e.g., { task, model, onProgress }) and update the
implementation to destructure that object; then update every call site to pass
an object with those named properties so order no longer matters and optional
onProgress is explicit. Ensure types are updated (use an interface/type for the
options), retain the return type Promise<PipelineInstance>, and update any
references to the parameter names inside getPipeline accordingly.

In `@src/utils/ai/transcription-format.ts`:
- Around line 24-27: The toSRT and toVTT functions currently return plain text
when result.segments is missing; update them to not emit untimed text for
subtitle formats by either synthesizing a single timed cue using result.duration
(use result.duration as the cue end time and 0 as start) or throw an error so
callers can fall back to the "text" format; locate the logic in toSRT and toVTT
that checks result.segments and replace the early return of result.text with
code that builds a single cue from result.duration (or throws if duration is
absent) while preserving TranscriptionResult handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 304f231f-1695-41d5-a5a2-fd96810981e3

📥 Commits

Reviewing files that changed from the base of the PR and between 0745210 and 992f13f.

📒 Files selected for processing (5)
  • src/e2e/voice-memos.e2e.test.ts
  • src/macos/commands/voice-memos/index.ts
  • src/transcribe/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/transcription-format.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Do not add file-path comments as the first line of files
Do not add obvious comments that restate what the code already says
Never use one-line if statements; always use block form with braces
Include an empty line before if statements, unless the preceding line is a variable declaration used by that if
Include an empty line after closing } unless followed by else, catch, finally, or another }
Use object parameters for functions with 3+ parameters or optional parameters
Use positional parameters only for 1-2 required and obvious parameters such as estimateTokens(text) or resolve(base, path)
Prefer error: err over error: err instanceof Error ? err.message : String(err) when the error field accepts unknown

Files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Do not use as any for type casting; use proper type narrowing, type guards, or explicit interfaces instead
Use discriminant checks for union types (e.g., entity.className === "User")

Files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
src/utils/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place general-purpose utility functions in src/utils/ instead of inside tool directories

Files:

  • src/utils/ai/transcription-format.ts
  • src/utils/ai/providers/AILocalProvider.ts
src/**/index.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/index.{ts,tsx}: Use commander for parsing command-line arguments with subcommands and options
Use @clack/prompts for interactive user experience in new tools; use @inquirer/prompts only for legacy tools
Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations
Use chalk for colored terminal output but strip ANSI codes for non-TTY environments
Use Bun.spawn() for executing external commands and handle stdout/stderr streams properly
Use Node.js path module for cross-platform path handling and resolve relative paths to absolute
Use Bun's native file APIs (Bun.write()) for better performance in file operations

Files:

  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
🧠 Learnings (32)
📓 Common learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Support multiple output destinations (file, clipboard, stdout) and use `clipboardy` for clipboard operations

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-02-20T00:52:27.023Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 31
File: src/ask/utils/helpers.ts:3-3
Timestamp: 2026-02-20T00:52:27.023Z
Learning: In all TypeScript source files under src, prefer using picocolors for colored terminal output in new code. Picocolors is smaller and faster than chalk, so adopt it for CLI output coloring and avoid adding chalk in new code paths unless there is a compelling compatibility reason.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:30:56.939Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:130-139
Timestamp: 2026-03-17T01:30:56.939Z
Learning: In genesiscz/GenesisTools, do not suggest converting two-argument functions with an optional second parameter (for example setMute(muted: boolean, app?: string)) to an object-parameter form. The project prefers simple positional parameters for short utility functions, even when an optional argument is present. The object-parameter guideline should only apply when a function has 3 or more parameters.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/utils/ai/transcription-format.ts
  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `Bun.spawn()` for executing external commands and handle stdout/stderr streams properly

Applied to files:

  • src/e2e/voice-memos.e2e.test.ts
  • src/transcribe/index.ts
📚 Learning: 2026-03-15T16:03:57.231Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:262-303
Timestamp: 2026-03-15T16:03:57.231Z
Learning: In `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`, the `durations` array in `calculateRunCost()` and `WorkflowSummary` intentionally stores one entry per job (job-level duration in seconds), not per-run wall-clock time. This matches GitHub's billing model (rounded up per job). The aggregated/top-N output represents total billable job time, not wall-clock run time. Do not flag this as an issue in future reviews.

Applied to files:

  • src/transcribe/index.ts
📚 Learning: 2026-03-15T15:54:08.510Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 105
File: src/ask/index.ts:66-74
Timestamp: 2026-03-15T15:54:08.510Z
Learning: In genesiscz/GenesisTools, manual argv._[0] dispatch in src/ask/index.ts (and similar entry-point files) is intentional for lazy loading: subcommand modules (e.g. ask/commands/configure) are imported via dynamic import() inside the dispatch branch rather than registered as Commander subcommands. This avoids eagerly loading all subcommand modules at startup. Do NOT flag this pattern as a violation of the "use Commander for subcommands" guideline; the lazy-loading benefit outweighs the missing Commander help/error-handling for these entry points.

Applied to files:

  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `commander` for parsing command-line arguments with subcommands and options

Applied to files:

  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `clack/prompts` for interactive user experience in new tools; use `inquirer/prompts` only for legacy tools

Applied to files:

  • src/transcribe/index.ts
  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-17T01:32:56.919Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:122-127
Timestamp: 2026-03-17T01:32:56.919Z
Learning: In genesiscz/GenesisTools, mute/config state (SayConfig, isMuted, setMute, getConfig, getConfigForRead) belongs at the CLI layer (src/say/) rather than in the TTS utility layer (src/utils/macos/tts.ts). The tts.ts file should remain a pure macOS TTS primitive (getVoiceMap, speak, listVoices, listVoicesStructured) without any application-level config or mute logic. The `app` field in SpeakOptions should also be removed since it only exists to support per-app muting.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use positional parameters only for 1-2 required and obvious parameters such as `estimateTokens(text)` or `resolve(base, path)`

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use Node.js `path` module for cross-platform path handling and resolve relative paths to absolute

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-11T17:13:17.240Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/utils/debugging-master/llm-log.ts:27-27
Timestamp: 2026-03-11T17:13:17.240Z
Learning: In genesiscz/GenesisTools, `src/utils/debugging-master/llm-log.ts` is intentionally self-contained with zero external dependencies (only Node.js builtins). Do not add imports from `app/utils/json` or any other internal module to this file. When native JSON methods must be used here, suppress the `noRestrictedGlobals` Biome rule with `// biome-ignore lint/style/noRestrictedGlobals: self-contained snippet with no external dependencies` on the specific line.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/utils/**/*.{ts,tsx} : Place general-purpose utility functions in `src/utils/` instead of inside tool directories

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-17T01:47:58.691Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:148-148
Timestamp: 2026-03-17T01:47:58.691Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation plan documents (design/intent descriptions), not production code. Do not raise code-quality, robustness, or implementation-detail review comments (e.g., race conditions, error handling, temp file strategy) on these plan files. Such concerns are addressed in the actual implementation files (e.g., src/**/*.ts).

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:47:59.002Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T14:37:47.990Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 87
File: docs/typescript-sdks/apple/github-repos-index.md:94-94
Timestamp: 2026-03-11T14:37:47.990Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), docs under `docs/typescript-sdks/apple/` (e.g., `github-repos-index.md`, `macos-node-api.md`) are auto-generated. Do not flag trivial markdown formatting issues (e.g., MD022 blank lines around headings, MD031, etc.) in these files, as they will not be manually fixed.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T17:12:27.285Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/clarity/ui/src/routes/settings.tsx:1-1
Timestamp: 2026-03-11T17:12:27.285Z
Learning: In genesiscz/GenesisTools, `SafeJSON` in `src/utils/json.ts` intentionally uses `comment-json.parse` for parsing (to handle `//` comments, trailing commas, unquoted keys) but `JSON.stringify` (native) for stringification. Do not flag `SafeJSON.stringify` usages as unnecessary — the intent is a unified drop-in for `JSON` that parses leniently but serialises as standard JSON.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-09T13:39:43.059Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:48:08.317Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:209-222
Timestamp: 2026-03-17T01:48:08.317Z
Learning: In genesiscz/GenesisTools, files under .claude/plans/ are implementation plan documents, not production code. Do not raise code-quality, consistency, or implementation issues (e.g., naming conflicts, missing migrations, architectural concerns) against these plan files. Issues in plan documents are expected to be addressed during actual implementation. Only flag genuinely problematic structural issues (e.g., broken links, missing required sections) if they would block implementation. This extends the existing .claude/ directory exemption beyond markdownlint to all review categories.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Prefer `error: err` over `error: err instanceof Error ? err.message : String(err)` when the error field accepts unknown

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use object parameters for functions with 3+ parameters or optional parameters

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
🧬 Code graph analysis (5)
src/utils/ai/transcription-format.ts (2)
src/transcribe/index.ts (5)
  • OutputFormat (32-32)
  • formatTimestamp (32-32)
  • toSRT (32-32)
  • toVTT (32-32)
  • formatOutput (32-32)
src/utils/json.ts (1)
  • SafeJSON (24-58)
src/e2e/voice-memos.e2e.test.ts (1)
src/e2e/helpers.ts (1)
  • runTool (18-34)
src/transcribe/index.ts (5)
src/utils/ai/transcription-format.ts (2)
  • OutputFormat (4-4)
  • formatOutput (54-65)
src/ask/audio/AudioProcessor.ts (1)
  • audioProcessor (381-381)
src/utils/ai/types.ts (1)
  • AIProviderType (1-1)
src/utils/ai/index.ts (1)
  • AI (27-58)
src/utils/clipboard.ts (1)
  • copyToClipboard (4-14)
src/macos/commands/voice-memos/index.ts (5)
src/utils/table.ts (1)
  • formatTable (13-47)
src/transcribe/index.ts (2)
  • OutputFormat (32-32)
  • formatOutput (32-32)
src/utils/ai/index.ts (1)
  • AI (27-58)
src/utils/ai/types.ts (1)
  • AIProviderType (1-1)
src/utils/clipboard.ts (1)
  • copyToClipboard (4-14)
src/utils/ai/providers/AILocalProvider.ts (4)
src/utils/ai/types.ts (11)
  • AITask (2-2)
  • AITranscriptionProvider (71-73)
  • AITranslationProvider (75-77)
  • AISummarizationProvider (79-81)
  • TranscribeOptions (31-40)
  • TranscriptionResult (42-47)
  • TranslateOptions (49-53)
  • TranslationResult (55-59)
  • SummarizeOptions (61-64)
  • SummarizationResult (66-69)
  • OnProgress (21-21)
src/utils/ai/index.ts (2)
  • LanguageDetector (20-20)
  • createLanguageDetector (20-20)
src/utils/ai/LanguageDetector.ts (2)
  • LanguageDetector (33-86)
  • createLanguageDetector (317-353)
src/utils/audio/converter.ts (1)
  • toFloat32Audio (11-23)
🪛 GitHub Actions: CI
src/transcribe/index.ts

[warning] 3-3: lint/assist: Imports and exports are not sorted. Safe fix: Organize Imports (Biome).

🔇 Additional comments (12)
src/e2e/voice-memos.e2e.test.ts (4)

1-6: Imports and setup look good.

Clean imports using bun:test and Node.js fs modules. The EXPORT_DIR constant in /tmp is appropriate for e2e test isolation.


8-13: Cleanup logic is correct.

The afterAll properly guards with existsSync before attempting removal, and uses recursive: true, force: true to handle nested content and missing paths gracefully.


15-54: Help tests are well-structured.

Good coverage of --help across all subcommands. The transcribe --help test (lines 27-43) thoroughly validates that all expected flags are documented in the help output.


66-71: Search and error handling tests look good.

  • Search with nonexistent query correctly expects exit 0 (no match is not an error)
  • Invalid ID tests properly validate exit code 1 and error messaging
  • The output.toLowerCase().toMatch(/no memo|not found/i) pattern robustly handles different error phrasings

Also applies to: 83-89, 97-102

src/macos/commands/voice-memos/index.ts (7)

1-19: LGTM!

Imports are well-organized and follow project conventions: picocolors for terminal colors, @clack/prompts for interactive UX, commander for CLI parsing, and resolve from node:path for absolute path handling.


82-95: LGTM!

Error handling correctly distinguishes between domain-specific VoiceMemosError (warning) and unexpected errors (error), with proper exit codes.


101-109: Hardcoded locale for date formatting.

The formatMemoDate function uses "en-US" locale. This was previously flagged as a nitpick. Consider using undefined to respect system locale if internationalization becomes relevant.


134-146: LGTM!

The resolveMemo helper provides clean validation with descriptive error messages.


313-350: transcribeAll does not perform AI transcription fallback.

Unlike transcribeOne, this function only extracts embedded transcripts and counts memos without transcripts but never performs AI transcription on them. Consider renaming to extractAllTranscripts or implementing AI fallback similar to transcribeOne.


352-355: LGTM!

Core search logic is correct. Output destination options should be added per the earlier comment on the command registration.


361-432: LGTM!

Interactive mode properly uses @clack/prompts with good UX patterns: intro banner, select menus with hints, text input with initial value, proper cancellation handling via p.isCancel(), and clean exit with p.outro().

src/utils/ai/transcription-format.ts (1)

6-14: Good timestamp normalization.

Computing the fields from totalMs prevents impossible ...1000 millisecond values from leaking into subtitle timestamps.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/macos/commands/voice-memos/index.ts`:
- Around line 240-257: The early-return branch for embedded transcripts (when
!opts.force and extractTranscript(memo.path) returns a transcript) prints
directly to stdout and returns, bypassing the shared output/clipboard handling;
change this so you build the transcript output into the same string/object that
the rest of the command uses and let the common output routine handle writing to
file/clipboard/console instead of directly calling console.log and returning.
Specifically, in the block using extractTranscript, stop using console.log and
the immediate return; instead assemble the formatted lines (using
pc.bold(memo.title), formatDuration(...) and segment.text) into the same
variable or pass them into the existing output handler used elsewhere in this
module so --output and --clipboard are honored while keeping the
embedded-transcript detection logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0a29ec9b-f3c4-4c3b-a0bc-68cf71542c61

📥 Commits

Reviewing files that changed from the base of the PR and between 992f13f and 07a8c13.

📒 Files selected for processing (3)
  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Do not add file-path comments as the first line of files
Do not add obvious comments that restate what the code already says
Never use one-line if statements; always use block form with braces
Include an empty line before if statements, unless the preceding line is a variable declaration used by that if
Include an empty line after closing } unless followed by else, catch, finally, or another }
Use object parameters for functions with 3+ parameters or optional parameters
Use positional parameters only for 1-2 required and obvious parameters such as estimateTokens(text) or resolve(base, path)
Prefer error: err over error: err instanceof Error ? err.message : String(err) when the error field accepts unknown

Files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Do not use as any for type casting; use proper type narrowing, type guards, or explicit interfaces instead
Use discriminant checks for union types (e.g., entity.className === "User")

Files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
src/**/index.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/index.{ts,tsx}: Use commander for parsing command-line arguments with subcommands and options
Use @clack/prompts for interactive user experience in new tools; use @inquirer/prompts only for legacy tools
Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations
Use chalk for colored terminal output but strip ANSI codes for non-TTY environments
Use Bun.spawn() for executing external commands and handle stdout/stderr streams properly
Use Node.js path module for cross-platform path handling and resolve relative paths to absolute
Use Bun's native file APIs (Bun.write()) for better performance in file operations

Files:

  • src/macos/commands/voice-memos/index.ts
src/utils/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place general-purpose utility functions in src/utils/ instead of inside tool directories

Files:

  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
🧠 Learnings (31)
📓 Common learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:122-127
Timestamp: 2026-03-17T01:32:56.919Z
Learning: In genesiscz/GenesisTools, mute/config state (SayConfig, isMuted, setMute, getConfig, getConfigForRead) belongs at the CLI layer (src/say/) rather than in the TTS utility layer (src/utils/macos/tts.ts). The tts.ts file should remain a pure macOS TTS primitive (getVoiceMap, speak, listVoices, listVoicesStructured) without any application-level config or mute logic. The `app` field in SpeakOptions should also be removed since it only exists to support per-app muting.
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `commander` for parsing command-line arguments with subcommands and options

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-17T01:32:56.919Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:122-127
Timestamp: 2026-03-17T01:32:56.919Z
Learning: In genesiscz/GenesisTools, mute/config state (SayConfig, isMuted, setMute, getConfig, getConfigForRead) belongs at the CLI layer (src/say/) rather than in the TTS utility layer (src/utils/macos/tts.ts). The tts.ts file should remain a pure macOS TTS primitive (getVoiceMap, speak, listVoices, listVoicesStructured) without any application-level config or mute logic. The `app` field in SpeakOptions should also be removed since it only exists to support per-app muting.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `clack/prompts` for interactive user experience in new tools; use `inquirer/prompts` only for legacy tools

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use positional parameters only for 1-2 required and obvious parameters such as `estimateTokens(text)` or `resolve(base, path)`

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use Node.js `path` module for cross-platform path handling and resolve relative paths to absolute

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-15T15:54:08.510Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 105
File: src/ask/index.ts:66-74
Timestamp: 2026-03-15T15:54:08.510Z
Learning: In genesiscz/GenesisTools, manual argv._[0] dispatch in src/ask/index.ts (and similar entry-point files) is intentional for lazy loading: subcommand modules (e.g. ask/commands/configure) are imported via dynamic import() inside the dispatch branch rather than registered as Commander subcommands. This avoids eagerly loading all subcommand modules at startup. Do NOT flag this pattern as a violation of the "use Commander for subcommands" guideline; the lazy-loading benefit outweighs the missing Commander help/error-handling for these entry points.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Support multiple output destinations (file, clipboard, stdout) and use `clipboardy` for clipboard operations

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-11T17:13:17.240Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/utils/debugging-master/llm-log.ts:27-27
Timestamp: 2026-03-11T17:13:17.240Z
Learning: In genesiscz/GenesisTools, `src/utils/debugging-master/llm-log.ts` is intentionally self-contained with zero external dependencies (only Node.js builtins). Do not add imports from `app/utils/json` or any other internal module to this file. When native JSON methods must be used here, suppress the `noRestrictedGlobals` Biome rule with `// biome-ignore lint/style/noRestrictedGlobals: self-contained snippet with no external dependencies` on the specific line.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/utils/**/*.{ts,tsx} : Place general-purpose utility functions in `src/utils/` instead of inside tool directories

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-02-24T22:38:46.781Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/watchman/index.ts:98-99
Timestamp: 2026-02-24T22:38:46.781Z
Learning: In src/watchman/index.ts, `as any` casts are necessary when calling fb-watchman Client methods (e.g., `client.command()`, `client.on("subscription", ...)`) because types/fb-watchman has incomplete type definitions. Using the local WatchmanResponse interface causes TS2769 errors. The biome-ignore comments document this justified exception to the general no-any guideline.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
📚 Learning: 2026-02-20T00:52:27.023Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 31
File: src/ask/utils/helpers.ts:3-3
Timestamp: 2026-02-20T00:52:27.023Z
Learning: In all TypeScript source files under src, prefer using picocolors for colored terminal output in new code. Picocolors is smaller and faster than chalk, so adopt it for CLI output coloring and avoid adding chalk in new code paths unless there is a compelling compatibility reason.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-17T01:30:56.939Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:130-139
Timestamp: 2026-03-17T01:30:56.939Z
Learning: In genesiscz/GenesisTools, do not suggest converting two-argument functions with an optional second parameter (for example setMute(muted: boolean, app?: string)) to an object-parameter form. The project prefers simple positional parameters for short utility functions, even when an optional argument is present. The object-parameter guideline should only apply when a function has 3 or more parameters.

Applied to files:

  • src/macos/commands/voice-memos/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
📚 Learning: 2026-03-17T01:47:58.691Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:148-148
Timestamp: 2026-03-17T01:47:58.691Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation plan documents (design/intent descriptions), not production code. Do not raise code-quality, robustness, or implementation-detail review comments (e.g., race conditions, error handling, temp file strategy) on these plan files. Such concerns are addressed in the actual implementation files (e.g., src/**/*.ts).

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:47:59.002Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T14:37:47.990Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 87
File: docs/typescript-sdks/apple/github-repos-index.md:94-94
Timestamp: 2026-03-11T14:37:47.990Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), docs under `docs/typescript-sdks/apple/` (e.g., `github-repos-index.md`, `macos-node-api.md`) are auto-generated. Do not flag trivial markdown formatting issues (e.g., MD022 blank lines around headings, MD031, etc.) in these files, as they will not be manually fixed.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T17:12:27.285Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/clarity/ui/src/routes/settings.tsx:1-1
Timestamp: 2026-03-11T17:12:27.285Z
Learning: In genesiscz/GenesisTools, `SafeJSON` in `src/utils/json.ts` intentionally uses `comment-json.parse` for parsing (to handle `//` comments, trailing commas, unquoted keys) but `JSON.stringify` (native) for stringification. Do not flag `SafeJSON.stringify` usages as unnecessary — the intent is a unified drop-in for `JSON` that parses leniently but serialises as standard JSON.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-09T13:39:43.059Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:48:08.317Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:209-222
Timestamp: 2026-03-17T01:48:08.317Z
Learning: In genesiscz/GenesisTools, files under .claude/plans/ are implementation plan documents, not production code. Do not raise code-quality, consistency, or implementation issues (e.g., naming conflicts, missing migrations, architectural concerns) against these plan files. Issues in plan documents are expected to be addressed during actual implementation. Only flag genuinely problematic structural issues (e.g., broken links, missing required sections) if they would block implementation. This extends the existing .claude/ directory exemption beyond markdownlint to all review categories.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Prefer `error: err` over `error: err instanceof Error ? err.message : String(err)` when the error field accepts unknown

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use object parameters for functions with 3+ parameters or optional parameters

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
🧬 Code graph analysis (1)
src/utils/ai/providers/AILocalProvider.ts (3)
src/utils/ai/types.ts (7)
  • TranscribeOptions (31-47)
  • TranscriptionResult (62-67)
  • TranslateOptions (69-73)
  • TranslationResult (75-79)
  • SummarizeOptions (81-84)
  • SummarizationResult (86-89)
  • OnProgress (21-21)
src/utils/ai/LanguageDetector.ts (2)
  • LanguageDetector (33-86)
  • createLanguageDetector (317-353)
src/utils/audio/converter.ts (1)
  • toFloat32Audio (11-23)
🔇 Additional comments (13)
src/utils/ai/types.ts (1)

1-106: Well-structured type definitions for the AI provider system.

The contracts are comprehensive and extensible:

  • Clear provider abstraction with AIProvider base and task-specific extensions
  • Progress/segment callbacks enable live streaming feedback
  • Whisper thresholds are well-documented for different audio scenarios
  • TaskConfig supports per-call provider overrides
src/utils/ai/providers/AILocalProvider.ts (8)

1-38: Clean provider setup with proper interface implementation.

The class correctly implements three provider interfaces, uses deduplication for concurrent pipeline loads via pendingPipelines, and properly declares supported tasks.


49-68: Resource leak: custom language detector not disposed on error.

If detector.detectFromAudio(audioData) throws at line 63, the custom detector created at line 55 won't be disposed.

🛡️ Wrap in try/finally
         if (options?.languageDetection) {
             detector = createLanguageDetector(options.languageDetection);
         } else {
             if (!this.langDetector) {
                 this.langDetector = createLanguageDetector();
             }

             detector = this.langDetector;
         }

-        const detected = await detector.detectFromAudio(audioData);
-        language = detected.language;
-
-        if (options?.languageDetection) {
-            detector.dispose();
+        try {
+            const detected = await detector.detectFromAudio(audioData);
+            language = detected.language;
+        } finally {
+            if (options?.languageDetection) {
+                detector.dispose();
+            }
         }

76-144: Solid transcription implementation with well-tuned Whisper parameters.

Good decisions:

  • whisper-large-v3-turbo over whisper-small for non-English quality
  • 29s chunks to avoid transformers.js bug #1358
  • Anti-hallucination params (condition_on_prev_tokens: false, no_repeat_ngram_size: 3) are essential for non-English
  • Per-module dtype preserves encoder precision while compressing decoder
  • Live chunk_callback enables streaming UX

158-165: Catch block collapses all failures into "unsupported language pair" error.

Network errors, cache corruption, and runtime exceptions are all rewritten as language-pair errors, making diagnosis harder.

Preserve original error cause
-        } catch {
+        } catch (err) {
+            const cause = err instanceof Error ? err.message : String(err);
             throw new Error(
-                `Unsupported language pair: ${from} → ${to}. ` +
-                    `Model "${model}" not found. Try an explicit --model option or pivot through English.`
+                `Failed to load translation model "${model}" for ${from ?? "auto"} → ${to}. ` +
+                    `Try --model or pivot through English. Cause: ${cause}`
             );
         }

177-189: Clean summarization implementation.

Properly handles optional maxLength and returns originalLength for context.


191-191: Consider refactoring getPipeline to object parameters.

getPipeline(task, model, onProgress?) has 3 parameters including an optional one. As per coding guidelines: "Use object parameters for functions with 3+ parameters or optional parameters."


205-277: Well-implemented pipeline loading with proper error recovery.

Good patterns:

  • In-flight load deduplication via pendingPipelines prevents duplicate downloads
  • Per-module dtype (fp16 encoder + q4 decoder) preserves ASR quality while compressing
  • Cache corruption detection triggers cleanup and re-download on retry
  • finally block correctly removes pending promise regardless of outcome

279-286: Proper resource cleanup.

Disposes all cached pipelines and the language detector, with errors safely caught to avoid cleanup failures blocking other disposals.

src/macos/commands/voice-memos/index.ts (4)

32-35: Good hardening of numeric CLI parsing.

Using (v) => parseInt(v, 10) for memo ID arguments is the right fix and avoids implicit-radix pitfalls.

Also applies to: 39-42, 47-50


203-206: --all mode behavior remains inconsistent with single-item transcription flow.

transcribeAll still does embedded extraction only and never invokes AI fallback or AI-related options, unlike transcribeOne.

Also applies to: 327-364


64-69: search still only writes to stdout.

This is still missing output destination options (--output, --clipboard) and destination wiring.

As per coding guidelines: Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations.


265-267: Provider value is still cast without runtime validation.

opts.provider is still cast directly to AIProviderType before AI.Transcriber.create, so invalid user input can bypass CLI-level validation.

@genesiscz
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (7)
src/utils/macos/voice-memos.ts (2)

226-229: ⚠️ Potential issue | 🟠 Major

Parse tsrp JSON in strict mode.

parseTsrpPayload() currently uses lenient parsing at Line 228 (SafeJSON.parse(json)), but this is a file-format boundary and should fail fast on malformed payloads.

Proposed fix
-        const data = SafeJSON.parse(json);
+        const data = SafeJSON.parse(json, { strict: true });
#!/bin/bash
# Verify strict-vs-lenient usage in this module.
rg -nP 'SafeJSON\.parse\(' src/utils/macos/voice-memos.ts

Based on learnings, use SafeJSON.parse(text, { strict: true }) at non-config JSON boundaries and reserve lenient parsing for user-authored config files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/voice-memos.ts` around lines 226 - 229, In parseTsrpPayload
replace the lenient SafeJSON.parse(json) call with strict parsing by calling
SafeJSON.parse(json, { strict: true }) so malformed TSRP payloads fail fast;
update the parseTsrpPayload function (the try/catch around SafeJSON.parse) to
use the strict option and ensure existing error handling still logs or returns
null as before.

123-146: ⚠️ Potential issue | 🟠 Major

Avoid eager full-file transcript probing during memo mapping.

At Line 145, rowToMemo() calls hasTranscript(filePath), which reads the full audio file (readFileSync at Line 351) for every memo returned by listMemos() (Line 414). This makes listing scale with total audio bytes and then searchMemos() can read files again via extractTranscript().

Defer transcript probing to explicit transcript operations, or make it opt-in for listing to avoid repeated full-file I/O on normal list/search entry paths.

Also applies to: 345-357, 400-415, 443-462

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/macos/voice-memos.ts` around lines 123 - 146, rowToMemo currently
calls hasTranscript(filePath) eagerly (via hasTranscript/readFileSync) causing
full-file I/O for every memo; remove that eager probe by not calling
hasTranscript inside rowToMemo and instead expose transcript probing as an
opt-in operation: add an optional flag/parameter to listMemos (or a separate
function) such as includeTranscriptPresence or probeTranscripts and only call
hasTranscript when that flag is true, and set VoiceMemo.hasTranscript to a
default (false or undefined) when not probing; update references to rowToMemo,
listMemos, extractTranscript, and any callers that expect hasTranscript to
either pass the new flag or explicitly call hasTranscript/extractTranscript when
needed.
src/github/commands/issue.ts (3)

618-618: 🛠️ Refactor suggestion | 🟠 Major

Remove obvious narration comments in this block.

The comments at Line 618 and Line 635 restate nearby code and should be removed.

Suggested cleanup
-            // Resolve file path
             const filepath = options.output
                 ? options.output
                 : join(
                       typeof options.save === "string" ? options.save : join(process.cwd(), ".claude", "github"),
                       `${owner}-${repo}-${number}.md`
                   );
@@
-            // No save — print full markdown to stdout
             console.log(formatIssue(outputData, "md", { noIndex: options.noIndex }));
As per coding guidelines, "Do not add obvious comments that restate what the code already says."

Also applies to: 635-635

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/github/commands/issue.ts` at line 618, In src/github/commands/issue.ts
remove the redundant narration comments that simply restate the code (the
comment text "Resolve file path" and the other nearby obvious comment) inside
the block handling file path resolution so the code is self-explanatory; delete
those two comments (do not change surrounding logic or variable names) to comply
with the guideline against obvious comments.

625-626: ⚠️ Potential issue | 🔴 Critical

Use dirname() for directory resolution before write.

The "/" string check fails for Windows-style paths, so parent directories may not be created before Bun.write.

Cross-platform fix
-import { join } from "node:path";
+import { dirname, join } from "node:path";
@@
-            const dir = filepath.includes("/") ? filepath.substring(0, filepath.lastIndexOf("/")) : ".";
+            const dir = dirname(filepath);
             if (dir && !existsSync(dir)) {
                 mkdirSync(dir, { recursive: true });
             }
#!/bin/bash
python - <<'PY'
filepath = r"foo\bar.md"
current = filepath[:filepath.rfind("/")] if "/" in filepath else "."
from pathlib import PureWindowsPath
expected = str(PureWindowsPath(filepath).parent)
print("current_logic_dir =", current)
print("expected_windows_dir =", expected)
assert current != expected, "Current logic should diverge on Windows-style paths"
PY
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/github/commands/issue.ts` around lines 625 - 626, The directory
resolution for writing uses a manual "/" check on filepath which fails for
Windows-style paths; replace that logic by using path.dirname(filepath)
(imported from the path module) to compute the parent directory, then check with
existsSync(dir) and create parents as needed before Bun.write; update the code
around the variables filepath and dir and the existence check (and any
subsequent Bun.write) to use the path.dirname result so it works cross-platform.

645-646: ⚠️ Potential issue | 🟡 Minor

Don’t enable save when save is explicitly false in md/json mode.

This branch currently treats save: false as enabled because it only checks for undefined.

Guard update
-        } else if (options.save !== undefined) {
-            const localDir = typeof options.save === "string" ? options.save : join(process.cwd(), ".claude", "github");
+        } else if (options.save !== false && options.save !== undefined) {
+            const localDir =
+                typeof options.save === "string" && options.save.length > 0
+                    ? options.save
+                    : join(process.cwd(), ".claude", "github");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/github/commands/issue.ts` around lines 645 - 646, The code currently
treats options.save === false as enabled because it only checks for !==
undefined; update the condition so the branch runs only when save is provided
and not explicitly false. Change the guard around the localDir creation (the
else if that references options.save and sets localDir) to check both
options.save !== undefined and options.save !== false (or equivalently
options.save !== false && typeof options.save !== "undefined") so that a literal
false skips the save behavior; keep the existing logic that handles string paths
(typeof options.save === "string") unchanged.
src/timer/index.ts (2)

390-405: ⚠️ Potential issue | 🟠 Major

Interactive mode still allows durations that overflow the background runner.

This validator only rejects <= 0. If the user enters anything above MAX_TIMEOUT_MS and then chooses Background, handleBackgroundRun() still feeds that value to setTimeout, so the timer can complete at the wrong time. Reuse the same upper-bound check here.

💡 Proposed fix
                     const ms = parseDuration(value);

                     if (ms <= 0) {
                         return "Invalid duration. Use formats like: 25m, 1h30m, 90s";
                     }
+
+                    if (ms > MAX_TIMEOUT_MS) {
+                        return "Duration too large (max ~24.8 days)";
+                    }
                 },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` around lines 390 - 405, The interactive Duration
validator in the withCancel -> p.text call currently only checks for <= 0 and
must also reject values > MAX_TIMEOUT_MS so Background runs can't get
overflowed; update the validate function (the validate inside the p.text call
that uses parseDuration) to check parsed ms against MAX_TIMEOUT_MS and return a
clear error like "Duration exceeds maximum allowed (MAX_TIMEOUT_MS) — choose a
smaller duration" when ms > MAX_TIMEOUT_MS, ensuring the same upper-bound used
by handleBackgroundRun/setTimeout is enforced in the prompt.

494-527: ⚠️ Potential issue | 🟠 Major

parseInt makes --repeat validation too permissive.

parseInt("1.5", 10) becomes 1, and parseInt("2abc", 10) becomes 2, so malformed values can pass the later integer check. Parse with Number() and reject non-whole inputs at the option boundary instead of truncating them.

💡 Proposed fix
-import { Command } from "commander";
+import { Command, InvalidOptionArgumentError } from "commander";
...
-    .option("--repeat <n>", "Number of cycles (Pomodoro)", (v) => parseInt(v, 10))
+    .option("--repeat <n>", "Number of cycles (Pomodoro)", (v) => {
+        const n = Number(v);
+
+        if (!Number.isInteger(n) || n < 1) {
+            throw new InvalidOptionArgumentError("must be a positive integer");
+        }
+
+        return n;
+    })
JavaScript parseInt behavior for strings like "1.5" and "2abc", and Commander v14's recommended way to reject invalid option arguments in an option parser.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/timer/index.ts` around lines 494 - 527, The option parser for "--repeat
<n>" currently uses parseInt which silently truncates/accepts malformed values;
change the option's parser to use Number() (or parseFloat then validate) and
immediately reject non-finite or non-integer values there so invalid inputs like
"1.5" or "2abc" never reach the action handler; update the option callback for
"--repeat" and ensure the action's later check for repeat (used where repeat is
read) still expects a positive integer and can assume it's valid if the option
parser accepted it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/timer/index.ts`:
- Around line 262-269: The check in isProcessAlive(pid) is insufficient because
process.kill(pid, 0) only verifies a PID exists and can lead to acting on a
reused PID; persist an extra identity token with each timer (e.g., a random UUID
stored in timer state and injected into the spawned timer process via env var or
a temp file) and change isProcessAlive to verify the running process actually
holds that token before returning true. Update listTimers() to only treat timers
as active if isProcessAlive verifies the token, and update cancelTimer() to
fetch and verify the timer's identity token before sending SIGTERM so you never
kill an unrelated process (use the same unique symbols: isProcessAlive,
listTimers, cancelTimer and the timer state identity field).
- Around line 229-255: handleBackgroundRun can throw inside the await loop and
currently skips removeActiveTimer(id), leaving stale timers; wrap the repeating
logic (the for loop and any awaited operations like fireCompletionActions and
storage.withConfigLock/getActiveTimers/saveActiveTimers) in a try block and call
removeActiveTimer(id) in a finally so the timer is always cleaned up even on
rejection; keep error propagation or logging as appropriate (e.g., rethrow or
log inside catch) but ensure removeActiveTimer(id) is executed in the finally
branch.

In `@src/utils/macos/voice-memos.ts`:
- Around line 178-183: The function findAtom(buffer, start, end, targetType)
should use a single object parameter to follow the guideline for 3+ params:
change its signature to accept an object (e.g., { buffer, start, end, targetType
}) and adjust internal references to destructure those properties; update every
call site (notably findNestedAtom) to pass a named-object instead of positional
args; also update the TypeScript types/interfaces for the parameter and ensure
any default/optional behavior is preserved when destructuring.

---

Duplicate comments:
In `@src/github/commands/issue.ts`:
- Line 618: In src/github/commands/issue.ts remove the redundant narration
comments that simply restate the code (the comment text "Resolve file path" and
the other nearby obvious comment) inside the block handling file path resolution
so the code is self-explanatory; delete those two comments (do not change
surrounding logic or variable names) to comply with the guideline against
obvious comments.
- Around line 625-626: The directory resolution for writing uses a manual "/"
check on filepath which fails for Windows-style paths; replace that logic by
using path.dirname(filepath) (imported from the path module) to compute the
parent directory, then check with existsSync(dir) and create parents as needed
before Bun.write; update the code around the variables filepath and dir and the
existence check (and any subsequent Bun.write) to use the path.dirname result so
it works cross-platform.
- Around line 645-646: The code currently treats options.save === false as
enabled because it only checks for !== undefined; update the condition so the
branch runs only when save is provided and not explicitly false. Change the
guard around the localDir creation (the else if that references options.save and
sets localDir) to check both options.save !== undefined and options.save !==
false (or equivalently options.save !== false && typeof options.save !==
"undefined") so that a literal false skips the save behavior; keep the existing
logic that handles string paths (typeof options.save === "string") unchanged.

In `@src/timer/index.ts`:
- Around line 390-405: The interactive Duration validator in the withCancel ->
p.text call currently only checks for <= 0 and must also reject values >
MAX_TIMEOUT_MS so Background runs can't get overflowed; update the validate
function (the validate inside the p.text call that uses parseDuration) to check
parsed ms against MAX_TIMEOUT_MS and return a clear error like "Duration exceeds
maximum allowed (MAX_TIMEOUT_MS) — choose a smaller duration" when ms >
MAX_TIMEOUT_MS, ensuring the same upper-bound used by
handleBackgroundRun/setTimeout is enforced in the prompt.
- Around line 494-527: The option parser for "--repeat <n>" currently uses
parseInt which silently truncates/accepts malformed values; change the option's
parser to use Number() (or parseFloat then validate) and immediately reject
non-finite or non-integer values there so invalid inputs like "1.5" or "2abc"
never reach the action handler; update the option callback for "--repeat" and
ensure the action's later check for repeat (used where repeat is read) still
expects a positive integer and can assume it's valid if the option parser
accepted it.

In `@src/utils/macos/voice-memos.ts`:
- Around line 226-229: In parseTsrpPayload replace the lenient
SafeJSON.parse(json) call with strict parsing by calling SafeJSON.parse(json, {
strict: true }) so malformed TSRP payloads fail fast; update the
parseTsrpPayload function (the try/catch around SafeJSON.parse) to use the
strict option and ensure existing error handling still logs or returns null as
before.
- Around line 123-146: rowToMemo currently calls hasTranscript(filePath) eagerly
(via hasTranscript/readFileSync) causing full-file I/O for every memo; remove
that eager probe by not calling hasTranscript inside rowToMemo and instead
expose transcript probing as an opt-in operation: add an optional flag/parameter
to listMemos (or a separate function) such as includeTranscriptPresence or
probeTranscripts and only call hasTranscript when that flag is true, and set
VoiceMemo.hasTranscript to a default (false or undefined) when not probing;
update references to rowToMemo, listMemos, extractTranscript, and any callers
that expect hasTranscript to either pass the new flag or explicitly call
hasTranscript/extractTranscript when needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9d8263b6-3220-4284-b593-e90ddc0f071f

📥 Commits

Reviewing files that changed from the base of the PR and between 07a8c13 and 3ec109d.

📒 Files selected for processing (6)
  • src/github/commands/issue.ts
  • src/github/types.ts
  • src/timer/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
  • src/utils/ai/types.ts
  • src/utils/macos/voice-memos.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Do not add file-path comments as the first line of files
Do not add obvious comments that restate what the code already says
Never use one-line if statements; always use block form with braces
Include an empty line before if statements, unless the preceding line is a variable declaration used by that if
Include an empty line after closing } unless followed by else, catch, finally, or another }
Use object parameters for functions with 3+ parameters or optional parameters
Use positional parameters only for 1-2 required and obvious parameters such as estimateTokens(text) or resolve(base, path)
Prefer error: err over error: err instanceof Error ? err.message : String(err) when the error field accepts unknown

Files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Do not use as any for type casting; use proper type narrowing, type guards, or explicit interfaces instead
Use discriminant checks for union types (e.g., entity.className === "User")

Files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
src/**/index.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/index.{ts,tsx}: Use commander for parsing command-line arguments with subcommands and options
Use @clack/prompts for interactive user experience in new tools; use @inquirer/prompts only for legacy tools
Support multiple output destinations (file, clipboard, stdout) and use clipboardy for clipboard operations
Use chalk for colored terminal output but strip ANSI codes for non-TTY environments
Use Bun.spawn() for executing external commands and handle stdout/stderr streams properly
Use Node.js path module for cross-platform path handling and resolve relative paths to absolute
Use Bun's native file APIs (Bun.write()) for better performance in file operations

Files:

  • src/timer/index.ts
src/utils/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place general-purpose utility functions in src/utils/ instead of inside tool directories

Files:

  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
🧠 Learnings (43)
📓 Common learnings
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:14:03.722Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), there is no CI formatter (e.g., Prettier/Biome) configured or enforced. Do not flag formatting mismatches based on static analysis tool annotations (e.g., GitHub Actions CI formatter warnings) in this repository, as they are not representative of actual enforced formatting rules. TypeScript files under src/ should be reviewed for logical correctness and consistency with existing code patterns instead.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:31.715Z
Learning: In genesiscz/GenesisTools (src/**/*.ts), do not add inline comments for well-known POSIX patterns or standard idioms (e.g., process.kill(pid, 0) probe) when the surrounding code is already self-documenting via descriptive function/variable names. The project enforces a no-obvious-comments rule: avoid comments that merely restate what the code clearly expresses.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:148-148
Timestamp: 2026-03-17T01:47:58.691Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation plan documents (design/intent descriptions), not production code. Do not raise code-quality, robustness, or implementation-detail review comments (e.g., race conditions, error handling, temp file strategy) on these plan files. Such concerns are addressed in the actual implementation files (e.g., src/**/*.ts).
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 78
File: .claude/github/reviews/pr-74-2026-03-03T02-02-09.md:0-0
Timestamp: 2026-03-08T23:00:34.621Z
Learning: In the GenesisTools repository, files under the `.claude/github/reviews/` directory (e.g., `.claude/github/reviews/pr-74-2026-03-03T02-02-09.md`) are PR review artifacts that should NOT be committed to the repository. Flag any such files appearing in a PR diff as unrelated stray artifacts that should be removed from the branch/commit.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:209-222
Timestamp: 2026-03-17T01:48:08.317Z
Learning: In genesiscz/GenesisTools, files under .claude/plans/ are implementation plan documents, not production code. Do not raise code-quality, consistency, or implementation issues (e.g., naming conflicts, missing migrations, architectural concerns) against these plan files. Issues in plan documents are expected to be addressed during actual implementation. Only flag genuinely problematic structural issues (e.g., broken links, missing required sections) if they would block implementation. This extends the existing .claude/ directory exemption beyond markdownlint to all review categories.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-16-ToolsEnhancements.md:131-131
Timestamp: 2026-03-17T01:47:54.498Z
Learning: In the genesiscz/GenesisTools repository, files under `.claude/plans/` (e.g., `.claude/plans/2026-03-16-ToolsEnhancements.md`) are implementation plan documents, not production code. Do not flag implementation-level issues (e.g., filename uniqueness, overwrite risks, edge cases) in these files, as such concerns are addressed in the actual implementation. Treat these documents as design artifacts only.
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-16-AITools.md:112-112
Timestamp: 2026-03-17T01:47:57.319Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), files under `.claude/plans/` (e.g., `.claude/plans/2026-03-16-AITools.md`) are implementation plan/design documents, not production code. Do not flag code quality issues (e.g., magic numbers, missing named constants, style concerns) in code snippets within these plan documents, as they are illustrative only and the actual implementation addresses such issues.
📚 Learning: 2026-02-20T00:52:27.023Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 31
File: src/ask/utils/helpers.ts:3-3
Timestamp: 2026-02-20T00:52:27.023Z
Learning: In all TypeScript source files under src, prefer using picocolors for colored terminal output in new code. Picocolors is smaller and faster than chalk, so adopt it for CLI output coloring and avoid adding chalk in new code paths unless there is a compelling compatibility reason.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-02-24T15:32:37.494Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/output.ts:109-113
Timestamp: 2026-02-24T15:32:37.494Z
Learning: In TypeScript files under src/, do not require a leading blank line before an if statement that is the first statement inside a function body (immediately after the function signature). The blank line rule should only apply to if statements that come after other statements within the function body. Apply this guideline consistently across TS files in src to reduce unnecessary vertical whitespace and keep concise function bodies.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-09T13:13:58.786Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 81
File: src/github/commands/get.ts:209-212
Timestamp: 2026-03-09T13:13:58.786Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), do not treat CI formatter warnings as enforceable formatting rules for TypeScript files under src/. Focus reviews on logical correctness and consistency with existing code patterns. For files under src (e.g., src/github/commands/get.ts), prioritize code structure, readability, naming, correctness, and adherence to project conventions over automated formatting warnings from CI tools.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:31.610Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/timely/utils/entry-processor.ts:0-0
Timestamp: 2026-03-12T01:26:31.610Z
Learning: In code paths where JSON is consumed, prefer strict RFC 8259 validation by using SafeJSON.parse(text, { strict: true }) instead of the lenient default. Apply this at non-config boundaries (e.g., API responses, JSONL, cache outputs, subprocess outputs). Reserve the lenient comment-json behavior only for user-authored config files that may legitimately contain comments or trailing commas. For src/timely/utils/entry-processor.ts and similar modules, replace or wrap JSON parsing with SafeJSON.parse(text, { strict: true }) unless you are explicitly handling config files that require comments.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:58:27.831Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 103
File: src/port/index.ts:137-144
Timestamp: 2026-03-12T01:58:27.831Z
Learning: In GenesisTools, apply a no-obvious-comments rule: do not add inline comments for well-known POSIX patterns or standard idioms (e.g., a process.kill(pid, 0) probe) when surrounding code is self-documenting through descriptive function/variable names. This guidance applies to TypeScript files under src (src/**/*.ts). Only include comments if they add non-obvious rationale, edge-case behavior, or explain complex logic that cannot be inferred from code alone.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-02-24T15:32:44.925Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:18-20
Timestamp: 2026-02-24T15:32:44.925Z
Learning: In TypeScript files, do not require a blank line between the opening brace of a function and the first statement if the first statement is the if statement immediately after the signature. The blank-line rule applies to separating an if from unrelated preceding code within the same block, not to spacing after the function opening brace. Apply this rule to all TS functions across the codebase.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:03.611Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/ask/lib/ChatSessionManager.ts:0-0
Timestamp: 2026-03-12T01:26:03.611Z
Learning: Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation in all non-config boundaries (API responses, JSONL, cache, subprocess output). The 3-arg form SafeJSON.parse(text, null, { strict: true }) is invalid and should not be used. Only lenient default (no options) is appropriate for user-authored config files that may contain comments/trailing commas. Apply this guideline across TypeScript files (src/**/*.ts) wherever SafeJSON.parse is used.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:18.985Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/claude/lib/history/search.ts:0-0
Timestamp: 2026-03-12T01:26:18.985Z
Learning: When using SafeJSON.parse in TypeScript code, prefer the two-argument form SafeJSON.parse(text, { strict: true }) to enable strict RFC 8259 validation via the native JSON.parse. Do NOT use the three-argument form SafeJSON.parse(text, null, { strict: true }). Apply strict parsing at remote/third-party API boundaries, JSONL parsing points, and subprocess output. Fall back to the lenient/default form only for user-authored config files that may legitimately contain comments or trailing commas. This pattern keeps strict validation where appropriate and preserves leniency for internal/config data.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:27.000Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/debugging-master/commands/tail.ts:0-0
Timestamp: 2026-03-12T01:26:27.000Z
Learning: In the genesiscz/GenesisTools repository, prefer using SafeJSON.parse(text, { strict: true }) (2-argument form) at all non-config JSON boundaries such as API responses, JSONL parsers, cache files, and subprocess stdout. Reserve the lenient default (SafeJSON.parse(text) with no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-12T01:26:24.859Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/history-sync.ts:0-0
Timestamp: 2026-03-12T01:26:24.859Z
Learning: In GenesisTools, ensure SafeJSON.parse is called with exactly two arguments. Use SafeJSON.parse(text, { strict: true }) for strict RFC 8259 validation, or pass a reviver function as the second argument. Do not call SafeJSON.parse(text, null, { strict: true }) since the function signature does not support a three-argument form. Apply this guideline to all TypeScript files that use SafeJSON.parse (e.g., src/utils/json.ts) and other related code.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:30:56.939Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:130-139
Timestamp: 2026-03-17T01:30:56.939Z
Learning: In genesiscz/GenesisTools, do not suggest converting two-argument functions with an optional second parameter (for example setMute(muted: boolean, app?: string)) to an object-parameter form. The project prefers simple positional parameters for short utility functions, even when an optional argument is present. The object-parameter guideline should only apply when a function has 3 or more parameters.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-02-24T15:39:14.492Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:182-210
Timestamp: 2026-02-24T15:39:14.492Z
Learning: Do not suggest replacing chalk with picocolors in src/github/* files. For the GenesisTools repository, chalk is the established standard for colored terminal output in the src/github toolset, and reviews should preserve this dependency across all TypeScript files under src/github (e.g., src/github/lib/*.ts, src/github/*.ts). If a review topic concerns color output, prefer chalk-specific patterns or compatibility guidance rather than introducing an alternative library.

Applied to files:

  • src/github/types.ts
  • src/github/commands/issue.ts
📚 Learning: 2026-02-24T15:39:14.492Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 54
File: src/github/lib/review-output.ts:182-210
Timestamp: 2026-02-24T15:39:14.492Z
Learning: In src/github/, the review output architecture should route to separate formatters: formatReviewTerminal() for colored terminal output, formatReviewMarkdown() for markdown, and formatReviewJSON() for JSON. The caller should select the appropriate formatter based on output mode flags, and avoid adding per-formatter TTY detection since the architecture handles terminal vs non-terminal formatting at the formatter orchestration level. This guideline applies to all TypeScript files under src/github that implement or reference the review output formatting logic.

Applied to files:

  • src/github/types.ts
  • src/github/commands/issue.ts
📚 Learning: 2026-03-08T18:38:36.013Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 80
File: src/github/commands/review.ts:290-306
Timestamp: 2026-03-08T18:38:36.013Z
Learning: In the GenesisTools repo, review sessions in src/github/lib/review-session.ts and src/github/commands/review.ts use a short-lived, read-only cache with a 7-day TTL. Sessions are not updated after reply/resolve mutations; users should re-fetch with 'tools github review <pr> --llm' to obtain fresh data from the GitHub API. Do not treat session staleness after mutations as a bug; this is an intentional design choice to keep the CLI simple. Applies to TypeScript files under src/github/.

Applied to files:

  • src/github/types.ts
  • src/github/commands/issue.ts
📚 Learning: 2026-03-12T03:48:42.474Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 104
File: src/darwinkit/index.ts:146-156
Timestamp: 2026-03-12T03:48:42.474Z
Learning: In TypeScript files that use Commander subcommands and exit after showing help, replace code after Command.help() with the pattern: call sub.outputHelp(); (returns void) followed by process.exit(0) or process.exit(1). This avoids TS7027 unreachable-code because Command.help() returns never. Apply this pattern in all src/**/*.ts files where subcommands need to display help before exiting.

Applied to files:

  • src/github/types.ts
  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/macos/voice-memos.ts
  • src/utils/ai/types.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use `Bun.spawn()` for executing external commands and handle stdout/stderr streams properly

Applied to files:

  • src/timer/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use object parameters for functions with 3+ parameters or optional parameters

Applied to files:

  • src/timer/index.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use positional parameters only for 1-2 required and obvious parameters such as `estimateTokens(text)` or `resolve(base, path)`

Applied to files:

  • src/timer/index.ts
📚 Learning: 2026-03-15T16:03:57.231Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:262-303
Timestamp: 2026-03-15T16:03:57.231Z
Learning: In `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`, the `durations` array in `calculateRunCost()` and `WorkflowSummary` intentionally stores one entry per job (job-level duration in seconds), not per-run wall-clock time. This matches GitHub's billing model (rounded up per job). The aggregated/top-N output represents total billable job time, not wall-clock run time. Do not flag this as an issue in future reviews.

Applied to files:

  • src/timer/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Do not add obvious comments that restate what the code already says

Applied to files:

  • src/timer/index.ts
  • src/github/commands/issue.ts
📚 Learning: 2026-03-15T16:03:33.870Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:59-67
Timestamp: 2026-03-15T16:03:33.870Z
Learning: In `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`, ubuntu-slim runner detection (1-core, $0.002/min) is intentionally omitted from `RUNNER_RATES` and `detectRunnerType`. The maintainer considers it rarely used in practice and has explicitly decided not to add detection for it. Do not flag its absence as an issue in future reviews.

Applied to files:

  • src/timer/index.ts
📚 Learning: 2026-03-15T15:54:08.510Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 105
File: src/ask/index.ts:66-74
Timestamp: 2026-03-15T15:54:08.510Z
Learning: In genesiscz/GenesisTools, manual argv._[0] dispatch in src/ask/index.ts (and similar entry-point files) is intentional for lazy loading: subcommand modules (e.g. ask/commands/configure) are imported via dynamic import() inside the dispatch branch rather than registered as Commander subcommands. This avoids eagerly loading all subcommand modules at startup. Do NOT flag this pattern as a violation of the "use Commander for subcommands" guideline; the lazy-loading benefit outweighs the missing Commander help/error-handling for these entry points.

Applied to files:

  • src/timer/index.ts
  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T16:03:50.341Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 106
File: plugins/genesis-tools/skills/github/scripts/actions-cost.ts:9-12
Timestamp: 2026-03-15T16:03:50.341Z
Learning: In genesiscz/GenesisTools, for standalone bun scripts (e.g., `plugins/genesis-tools/skills/github/scripts/actions-cost.ts`) that are invoked directly, the correct convention is `bun file.ts` (not `bun run file.ts`). `bun run` is reserved for package.json script entries. The shebang `#!/usr/bin/env bun` is used to mark such standalone scripts. Do NOT suggest replacing `bun file.ts` with `bun run file.ts` in usage/help text for standalone scripts.

Applied to files:

  • src/timer/index.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to src/**/index.{ts,tsx} : Use Node.js `path` module for cross-platform path handling and resolve relative paths to absolute

Applied to files:

  • src/github/commands/issue.ts
📚 Learning: 2026-03-17T01:47:58.691Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:148-148
Timestamp: 2026-03-17T01:47:58.691Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation plan documents (design/intent descriptions), not production code. Do not raise code-quality, robustness, or implementation-detail review comments (e.g., race conditions, error handling, temp file strategy) on these plan files. Such concerns are addressed in the actual implementation files (e.g., src/**/*.ts).

Applied to files:

  • src/github/commands/issue.ts
  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-02-15T17:29:24.888Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 27
File: .claude/old/commands/implement-v2.md:10-12
Timestamp: 2026-02-15T17:29:24.888Z
Learning: Files in the `.claude/old/` directory are archived for historical reference and should be preserved as-is. Do not suggest fixes or cleanup for files in this directory, including markdown linting issues or code formatting changes.

Applied to files:

  • src/github/commands/issue.ts
📚 Learning: 2026-03-08T23:00:34.621Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 78
File: .claude/github/reviews/pr-74-2026-03-03T02-02-09.md:0-0
Timestamp: 2026-03-08T23:00:34.621Z
Learning: In the GenesisTools repository, files under the `.claude/github/reviews/` directory (e.g., `.claude/github/reviews/pr-74-2026-03-03T02-02-09.md`) are PR review artifacts that should NOT be committed to the repository. Flag any such files appearing in a PR diff as unrelated stray artifacts that should be removed from the branch/commit.

Applied to files:

  • src/github/commands/issue.ts
📚 Learning: 2026-03-12T01:26:26.145Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/macos/lib/mail/jxa.ts:0-0
Timestamp: 2026-03-12T01:26:26.145Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. To enable strict RFC 8259 validation (delegating to native `JSON.parse`), pass `{ strict: true }` as the second argument: `SafeJSON.parse(text, { strict: true })`. There is NO 3-argument overload — do NOT suggest `SafeJSON.parse(text, null, { strict: true })`. Use strict mode at all remote/third-party API response boundaries, JSONL parsing points, subprocess stdout, and cache files; reserve lenient default (no second arg) only for user-authored config files that may contain comments or trailing commas.

Applied to files:

  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:16.671Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/cache.ts:0-0
Timestamp: 2026-03-12T01:26:16.671Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second parameter is a union of reviver function or options object. When `{ strict: true }` is passed as the second argument, it delegates to native `JSON.parse` instead of `comment-json`'s lenient parser. Use `SafeJSON.parse(text, { strict: true })` at remote/third-party API response boundaries, JSONL parsing points, machine-written cache files, and subprocess output that need strict RFC 8259 validation. The lenient default (no second arg or `null`) is only appropriate for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:12.215Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/api.ts:0-0
Timestamp: 2026-03-12T01:26:12.215Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument can be either a reviver function or a `ParseOptions` object (e.g., `{ strict: true }`). When `{ strict: true }` is passed, it delegates to native `JSON.parse` instead of `comment-json`'s lenient parser. Use `SafeJSON.parse(text, { strict: true })` (NOT the 3-arg form with `null`) at remote/third-party API response boundaries, JSONL parsing points, cache files, and subprocess output that need strict RFC 8259 validation. Fall back to lenient default (no options) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:41.292Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/utils/clarity/api.ts:0-0
Timestamp: 2026-03-12T01:26:41.292Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument can be either a reviver function or an options object (e.g. `{ strict: true }`). Use `SafeJSON.parse(text, { strict: true })` at remote/third-party API response boundaries (e.g. Clarity API, Timely API, Azure DevOps API) and JSONL/subprocess-output parsing points so malformed or non-standard responses fail fast. Fall back to the lenient default (no second arg, or `null`) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:47.022Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/azure-devops/commands/timelog/import.ts:0-0
Timestamp: 2026-03-12T01:26:47.022Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts exactly TWO arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument is overloaded: pass a function for a reviver, or pass `{ strict: true }` / `{ jsonl: true }` as a `ParseOptions` object to delegate to native `JSON.parse`. The correct strict-mode call is `SafeJSON.parse(text, { strict: true })` — NOT the 3-arg form `SafeJSON.parse(text, null, { strict: true })`. Use strict mode at remote/third-party API response boundaries, JSONL parsing points, cache files, and subprocess output. Reserve the lenient default (no options) for user-authored config files that may contain comments or trailing commas.

Applied to files:

  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:09.054Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/automate/lib/step-runner.ts:68-72
Timestamp: 2026-03-12T01:26:09.054Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second parameter is either a reviver function or an options object (e.g., `{ strict: true }`). To enforce strict RFC 8259 validation at remote/third-party API response boundaries, subprocess stdout, JSONL parsing, and cache files, use `SafeJSON.parse(text, { strict: true })`. Do NOT pass a null placeholder before the options object — the 3-arg form does not exist. The `JSON.parse` call inside the strict branch must be suppressed with `// biome-ignore lint/style/noRestrictedGlobals: intentional strict-mode fallback to native JSON.parse`.

Applied to files:

  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:30.872Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/hold-ai/client.ts:0-0
Timestamp: 2026-03-12T01:26:30.872Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument can be either a reviver function or a `ParseOptions` object (e.g., `{ strict: true }`). To enable strict RFC 8259 validation (delegating to native `JSON.parse`), use `SafeJSON.parse(text, { strict: true })` — there is no separate third `options` parameter. Apply `{ strict: true }` at all non-config boundaries: API responses, JSONL parsing, cache files, and subprocess output. Reserve lenient default (no options) for user-authored config files that may contain comments or trailing commas.

Applied to files:

  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-12T01:26:38.771Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/utils/claude/auth.ts:0-0
Timestamp: 2026-03-12T01:26:38.771Z
Learning: In genesiscz/GenesisTools, `SafeJSON.parse` in `src/utils/json.ts` accepts two arguments: `(text: string, reviverOrOptions?: Reviver | ParseOptions | null)`. The second argument can be either a reviver function or a `ParseOptions` object (e.g., `{ strict: true }`). There is no separate third `options` argument. Use `SafeJSON.parse(text, { strict: true })` at external/API/subprocess/JSONL/cache-file boundaries so malformed data fails fast with strict RFC 8259 validation. Use the lenient default (no second arg) only for user-authored config files that may legitimately contain comments or trailing commas.

Applied to files:

  • src/utils/macos/voice-memos.ts
📚 Learning: 2026-03-17T01:32:56.919Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: src/utils/macos/tts.ts:122-127
Timestamp: 2026-03-17T01:32:56.919Z
Learning: In genesiscz/GenesisTools, mute/config state (SayConfig, isMuted, setMute, getConfig, getConfigForRead) belongs at the CLI layer (src/say/) rather than in the TTS utility layer (src/utils/macos/tts.ts). The tts.ts file should remain a pure macOS TTS primitive (getVoiceMap, speak, listVoices, listVoicesStructured) without any application-level config or mute logic. The `app` field in SpeakOptions should also be removed since it only exists to support per-app muting.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:47:59.002Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-TeamRules.md:15-15
Timestamp: 2026-03-17T01:47:59.002Z
Learning: In genesiscz/GenesisTools, files under `.claude/plans/` are implementation planning documents, not production code. They may contain superseded or internally inconsistent details across different plan files (e.g., an alias name evolving from `sayy` to `speak`). Do not flag internal inconsistencies between plan documents — the actual implementation in the source code takes precedence. Avoid raising review comments on `.claude/plans/` files for inconsistencies that are resolved in the code.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T14:37:47.990Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 87
File: docs/typescript-sdks/apple/github-repos-index.md:94-94
Timestamp: 2026-03-11T14:37:47.990Z
Learning: In the GenesisTools repo (genesiscz/GenesisTools), docs under `docs/typescript-sdks/apple/` (e.g., `github-repos-index.md`, `macos-node-api.md`) are auto-generated. Do not flag trivial markdown formatting issues (e.g., MD022 blank lines around headings, MD031, etc.) in these files, as they will not be manually fixed.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-11T17:12:27.285Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 95
File: src/clarity/ui/src/routes/settings.tsx:1-1
Timestamp: 2026-03-11T17:12:27.285Z
Learning: In genesiscz/GenesisTools, `SafeJSON` in `src/utils/json.ts` intentionally uses `comment-json.parse` for parsing (to handle `//` comments, trailing commas, unquoted keys) but `JSON.stringify` (native) for stringification. Do not flag `SafeJSON.stringify` usages as unnecessary — the intent is a unified drop-in for `JSON` that parses leniently but serialises as standard JSON.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-09T13:39:43.059Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 82
File: src/claude/lib/usage/dashboard-config.ts:24-25
Timestamp: 2026-03-09T13:39:43.059Z
Learning: GenesisTools (genesiscz/GenesisTools) is a personal CLI tool, not a multi-tenant service. Do not suggest data migration paths or backward-compatibility shims for simple default value changes in configuration files (e.g., dashboard-config.ts). The TUI provides interactive controls (e.g., the `i` key to cycle poll intervals at runtime), so users can adjust settings without needing automated migrations. Avoid overengineering suggestions in this context.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-17T01:48:08.317Z
Learnt from: genesiscz
Repo: genesiscz/GenesisTools PR: 107
File: .claude/plans/2026-03-17-Plan1-NotifyAndSay.md:209-222
Timestamp: 2026-03-17T01:48:08.317Z
Learning: In genesiscz/GenesisTools, files under .claude/plans/ are implementation plan documents, not production code. Do not raise code-quality, consistency, or implementation issues (e.g., naming conflicts, missing migrations, architectural concerns) against these plan files. Issues in plan documents are expected to be addressed during actual implementation. Only flag genuinely problematic structural issues (e.g., broken links, missing required sections) if they would block implementation. This extends the existing .claude/ directory exemption beyond markdownlint to all review categories.

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
📚 Learning: 2026-03-15T23:02:14.981Z
Learnt from: CR
Repo: genesiscz/GenesisTools PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T23:02:14.981Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Prefer `error: err` over `error: err instanceof Error ? err.message : String(err)` when the error field accepts unknown

Applied to files:

  • src/utils/ai/providers/AILocalProvider.ts
🧬 Code graph analysis (5)
src/timer/index.ts (3)
src/utils/table.ts (1)
  • formatTable (13-47)
src/utils/prompts/clack/helpers.ts (2)
  • p (77-77)
  • withCancel (27-33)
src/utils/format.ts (1)
  • parseDuration (123-159)
src/github/commands/issue.ts (1)
src/github/types.ts (1)
  • CommentPreview (217-220)
src/utils/macos/voice-memos.ts (3)
src/utils/terminal.ts (1)
  • detectTerminalApp (6-31)
src/utils/macos/MacOS.ts (1)
  • MacOS (3-5)
src/utils/json.ts (1)
  • SafeJSON (24-58)
src/utils/ai/types.ts (1)
src/utils/macos/voice-memos.ts (1)
  • TranscriptionResult (28-31)
src/utils/ai/providers/AILocalProvider.ts (3)
src/utils/ai/types.ts (10)
  • AITask (2-2)
  • AITranscriptionProvider (105-107)
  • AITranslationProvider (109-111)
  • AISummarizationProvider (113-115)
  • TranscribeOptions (31-47)
  • TranscriptionResult (62-67)
  • TranslateOptions (83-87)
  • TranslationResult (89-93)
  • SummarizeOptions (95-98)
  • SummarizationResult (100-103)
src/utils/ai/LanguageDetector.ts (2)
  • LanguageDetector (33-86)
  • createLanguageDetector (317-353)
src/utils/audio/converter.ts (1)
  • toFloat32Audio (11-23)
🪛 GitHub Actions: CI
src/timer/index.ts

[warning] 493-507: Formatter would have printed content; code formatting differences detected by linter. Apply formatting fixes.

src/utils/macos/voice-memos.ts

[error] 176-183: Formatter would have printed the following content: formatting changes detected by linter.

genesiscz added a commit that referenced this pull request Mar 18, 2026
- Use Number.isNaN instead of global isNaN (say, CI lint fix)
- Preserve error cause in translation model catch block (AILocalProvider)
- Wrap language detector in try/finally for disposal on error (AILocalProvider)
- Use dirname() instead of lastIndexOf("/") for path handling (github/issue)
- Handle 32-bit WAV alignment and Int32 PCM format (audio/converter)
- Add millisecond carry-over boundary test (youtube/transcribe)
- Fail fast on srt/vtt without segments (youtube/transcribe)
- Fix clipboard also dumping to stdout (transcribe)
- Wrap background timer loop in try/finally for cleanup (timer)
- Inline output variable in e2e test (voice-memos)
- Add timeout for --all transcribe test (voice-memos)
genesiscz added a commit that referenced this pull request Mar 18, 2026
- t14: use `typeset` instead of `local` at top-level in dotdotdot.ts
- t17: guard DEBUG trap against internal hook functions in notify
- t16: normalize TERM_PROGRAM to match AppleScript app names
- t20/t21: add negatable --no-ignore-dnd/--no-say flags, fix help()
- t30: decode WAV audio before passing to Whisper pipeline
- t31: wrap translation model lookup in try/catch with helpful error
- t32: rename getAvailableProviders() to getAllProviders()
- t18: remove hardcoded /opt/homebrew path in benchmark
- t22: use parseAsync() in say CLI for proper error handling
- t23: use full voice list instead of deduplicated map
genesiscz added a commit that referenced this pull request Mar 18, 2026
- t34/t35: isNaN → Number.isNaN in timer
- t36/t42: fix ms overflow in formatTimestamp (compute from totalMs)
- t38: use parseAsync() in transcribe CLI
- t33: update voice-memos usage docs
- t44: require --from for local translation instead of defaulting to English
- t45: memoize in-flight pipeline loads to prevent duplicate downloads
- t46: use LBUFFER/RBUFFER in dotdotdot collapse to preserve cursor position
- t31: clean up temp file after interactive summarize
genesiscz added a commit that referenced this pull request Mar 18, 2026
- fix(ai): correct HuggingFace cache path construction (no-op replace bug)
- fix(youtube): prevent process.exit() from skipping finally cleanup
- fix(youtube): use template literals for string concatenation
- fix(ai): use template literal in HF token log
- fix(timer): refactor to options objects, add duration/repeat validation
- fix(timer): persist cycle progress in background repeat timers
- fix(timer): use withConfigLock for timer cleanup in list
- fix(voice-memos): add radix 10 to parseInt calls
- fix(audio): clean up temp WAV file on conversion error
- fix(audio): tighten test assertion for garbage input rejection
- fix(terminal): trim redundant JSDoc line
genesiscz added a commit that referenced this pull request Mar 18, 2026
- Use Number.isNaN instead of global isNaN (say, CI lint fix)
- Preserve error cause in translation model catch block (AILocalProvider)
- Wrap language detector in try/finally for disposal on error (AILocalProvider)
- Use dirname() instead of lastIndexOf("/") for path handling (github/issue)
- Handle 32-bit WAV alignment and Int32 PCM format (audio/converter)
- Add millisecond carry-over boundary test (youtube/transcribe)
- Fail fast on srt/vtt without segments (youtube/transcribe)
- Fix clipboard also dumping to stdout (transcribe)
- Wrap background timer loop in try/finally for cleanup (timer)
- Inline output variable in e2e test (voice-memos)
- Add timeout for --all transcribe test (voice-memos)
genesiscz added a commit that referenced this pull request Mar 18, 2026
- t14: use `typeset` instead of `local` at top-level in dotdotdot.ts
- t17: guard DEBUG trap against internal hook functions in notify
- t16: normalize TERM_PROGRAM to match AppleScript app names
- t20/t21: add negatable --no-ignore-dnd/--no-say flags, fix help()
- t30: decode WAV audio before passing to Whisper pipeline
- t31: wrap translation model lookup in try/catch with helpful error
- t32: rename getAvailableProviders() to getAllProviders()
- t18: remove hardcoded /opt/homebrew path in benchmark
- t22: use parseAsync() in say CLI for proper error handling
- t23: use full voice list instead of deduplicated map
genesiscz added a commit that referenced this pull request Mar 18, 2026
- t34/t35: isNaN → Number.isNaN in timer
- t36/t42: fix ms overflow in formatTimestamp (compute from totalMs)
- t38: use parseAsync() in transcribe CLI
- t33: update voice-memos usage docs
- t44: require --from for local translation instead of defaulting to English
- t45: memoize in-flight pipeline loads to prevent duplicate downloads
- t46: use LBUFFER/RBUFFER in dotdotdot collapse to preserve cursor position
- t31: clean up temp file after interactive summarize
genesiscz added a commit that referenced this pull request Mar 18, 2026
- fix(ai): correct HuggingFace cache path construction (no-op replace bug)
- fix(youtube): prevent process.exit() from skipping finally cleanup
- fix(youtube): use template literals for string concatenation
- fix(ai): use template literal in HF token log
- fix(timer): refactor to options objects, add duration/repeat validation
- fix(timer): persist cycle progress in background repeat timers
- fix(timer): use withConfigLock for timer cleanup in list
- fix(voice-memos): add radix 10 to parseInt calls
- fix(audio): clean up temp WAV file on conversion error
- fix(audio): tighten test assertion for garbage input rejection
- fix(terminal): trim redundant JSDoc line
genesiscz added a commit that referenced this pull request Mar 18, 2026
- Use Number.isNaN instead of global isNaN (say, CI lint fix)
- Preserve error cause in translation model catch block (AILocalProvider)
- Wrap language detector in try/finally for disposal on error (AILocalProvider)
- Use dirname() instead of lastIndexOf("/") for path handling (github/issue)
- Handle 32-bit WAV alignment and Int32 PCM format (audio/converter)
- Add millisecond carry-over boundary test (youtube/transcribe)
- Fail fast on srt/vtt without segments (youtube/transcribe)
- Fix clipboard also dumping to stdout (transcribe)
- Wrap background timer loop in try/finally for cleanup (timer)
- Inline output variable in e2e test (voice-memos)
- Add timeout for --all transcribe test (voice-memos)
@genesiscz genesiscz merged commit 2001bf1 into master Mar 18, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant