Skip to content

feat(ai): add local CLI enhancement provider#600

Closed
mitsuhiko wants to merge 1 commit intoBeingpax:mainfrom
mitsuhiko:local-cli-enhancement
Closed

feat(ai): add local CLI enhancement provider#600
mitsuhiko wants to merge 1 commit intoBeingpax:mainfrom
mitsuhiko:local-cli-enhancement

Conversation

@mitsuhiko
Copy link
Copy Markdown
Contributor

@mitsuhiko mitsuhiko commented Mar 20, 2026

Feel free to close this as this might be too sloppy for inclusion.

This is my attempt to implement post processing via local coding agents #591. It works quite well for me but it might not be generalizable.

This has the benefit that you can use your Codex or Claude subscriptions for it.

image

These environment variables are available to the script:

  • VOICEINK_SYSTEM_PROMPT: the system prompt
  • VOICEINK_USER_PROMPT: the user prompt (transcribed text)
  • VOICEINK_FULL_PROMPT: combined system + user prompt
  • VOICEINK_ACTIVE_APP_NAME: name of the frontmost application
  • VOICEINK_ACTIVE_APP_BUNDLE_ID: bundle identifier of the frontmost app
  • VOICEINK_ACTIVE_APP_PID: process ID of the frontmost app
  • VOICEINK_BROWSER_URL: current browser URL (if applicable)
  • VOICEINK_PROMPT_NAME: name of the active prompt profile
  • VOICEINK_SCREENSHOT: path to a screenshot of the active window (if captured)
  • VOICEINK_CLIPBOARD_TEXT: current clipboard contents
  • VOICEINK_SELECTED_TEXT: currently selected text in the frontmost app
  • VOICEINK_LOCALE: system locale (e.g. en_US)
  • VOICEINK_POWER_MODE: name of the active Power Mode (if any)
  • VOICEINK_RAW_SYSTEM_PROMPT: the prompt text without added context section

Summary by cubic

Adds a Local CLI enhancement provider that runs your own command-line tools to post‑process transcriptions. Includes templates for pi, claude, and codex, a settings UI, and clearer error messages.

  • New Features

    • New provider .localCLI (no API key). Considered connected when a command is set.
    • Command templates for pi, claude, codex with an editor and timeout picker in settings.
    • Executes via zsh login shell and injects VOICEINK_SYSTEM_PROMPT, VOICEINK_USER_PROMPT, VOICEINK_FULL_PROMPT; also writes the full prompt to stdin.
    • PATH auto-discovery for CLI binaries, output cleanup, and detailed errors (not configured, command not found, timeout, non‑zero exit, empty output).
    • Integrated into enhancement flow; errors surface with a short reason in notifications.
  • Migration

    • In Settings, select “Local CLI” as the provider.
    • Load a template or paste your command; use absolute paths if needed.
    • Adjust timeout as needed.
    • Verify your CLI (pi, claude, codex, etc.) is installed and available on your PATH.

Written for commit 1dc5c8f. Summary will update on new commits.

Add a Local CLI provider for AI enhancement with command templates for Pi, Claude, and Codex.

Include Local CLI settings in the provider UI with template loading, command editing, and timeout selection.

Wire enhancement execution through LocalCLIService with env-based prompt injection, stdin fallback, shell PATH discovery, and improved Local CLI error reporting in the transcription pipeline.
moona3k added a commit to moona3k/macparakeet that referenced this pull request Apr 3, 2026
## What Changed
- Sources/MacParakeetCore/Models/LLMProvider.swift: Added `.localCLI` case to
  `LLMProviderID` with `displayName`, `isLocal: false`, and `requiresAPIKey`
  property. Added `.localCLI()` factory method with placeholder config.
- Sources/MacParakeetCore/Services/LocalCLIExecutor.swift: New file containing
  `LocalCLIConfig`, `LocalCLITemplate` (Claude Code, Codex), `LocalCLIError`,
  `LocalCLIConfigStore`, and `LocalCLIExecutor` with PATH discovery, Process
  execution, env var injection, stdin piping, and timeout handling.
- Sources/MacParakeetCore/Services/LocalCLILLMClient.swift: New file implementing
  `LLMClientProtocol` for CLI tools. Formats messages into system/user prompts,
  delegates to `LocalCLIExecutor`, maps errors to `LLMError.cliError`.
- Sources/MacParakeetCore/Services/RoutingLLMClient.swift: New thin router (~40
  lines) that delegates `.localCLI` to `LocalCLILLMClient` and everything else
  to `LLMClient` (HTTP). Clean separation of concerns.
- Sources/MacParakeetCore/Services/LLMError.swift: Added `.cliError(String)` case.
- Sources/MacParakeetCore/Services/LLMConfigStore.swift: Skip Keychain operations
  for `.localCLI` provider (no API key needed).
- Sources/MacParakeet/App/AppEnvironment.swift: Changed `LLMClient()` to
  `RoutingLLMClient()` — single line change that enables CLI routing.
- Sources/MacParakeetViewModels/LLMSettingsDraft.swift: Added CLI fields
  (commandTemplate, selectedCLITemplate, cliTimeoutSeconds), validation for
  missing command, updated `requiresAPIKey` to use provider property.
- Sources/MacParakeetViewModels/LLMSettingsViewModel.swift: Added CLI config
  management (template selection, command editing, timeout), save/load via
  `LocalCLIConfigStore`, updated provider switching logic.
- Sources/MacParakeet/Views/Settings/LLMSettingsView.swift: CLI-specific settings
  UI with template picker, monospace command editor, timeout field, and custom
  privacy message. Hides model picker and base URL for CLI provider.
- Sources/CLI/Commands/LLMInlineConfig.swift: Added `--command` option and
  `.localCLI` case in `buildConfig()`.
- Tests: 44 new tests across LocalCLIExecutorTests, LocalCLILLMClientTests,
  and LLMSettingsViewModelTests (1064 total, all passing).

## Root Intent
Users with Claude Code or Codex subscriptions should be able to use their CLI
tools (`claude -p`, `codex exec`) for AI text enhancement — summaries, chat,
and transforms — instead of paying for separate API keys. This is a natural
extension of the existing LLM provider system.

Inspired by Beingpax/VoiceInk#591 (filed by @mitsuhiko) and
Beingpax/VoiceInk#600. Tracks #45.

## Prompt That Would Produce This Diff
Add a "Local CLI" LLM provider to MacParakeet that lets users run CLI tools
like `claude -p` or `codex exec` for AI features (summary, chat, transform).

Architecture: Create a separate `LocalCLILLMClient` conforming to
`LLMClientProtocol` (don't modify `LLMClient` which stays pure HTTP). Add a
thin `RoutingLLMClient` router that delegates `.localCLI` to the CLI client
and everything else to the HTTP client. Wire it via `AppEnvironment`.

The executor should: discover user's shell PATH via `zsh -ilc` (apps from
Finder don't get Homebrew paths), run commands via `/bin/zsh -lc`, set
MACPARAKEET_SYSTEM_PROMPT/USER_PROMPT/FULL_PROMPT env vars, pipe full prompt
to stdin, handle timeout with semaphore, and map exit codes to clear errors.

Add built-in templates for Claude Code and Codex. Store CLI config
(command + timeout) separately in UserDefaults since it doesn't fit
`LLMProviderConfig`. Add settings UI with template picker, monospace command
editor, and timeout field. Update CLI inline options with --command flag.

Set `isLocal: false` (CLI tools send data to cloud APIs) but
`requiresAPIKey: false`. Add tests for executor, client, and ViewModel.

## Files Changed
- Sources/MacParakeetCore/Services/LocalCLIExecutor.swift (+247)
- Sources/MacParakeetCore/Services/LocalCLILLMClient.swift (+76)
- Sources/MacParakeetCore/Services/RoutingLLMClient.swift (+43)
- Sources/MacParakeetCore/Models/LLMProvider.swift (+24, ~4)
- Sources/MacParakeetCore/Services/LLMError.swift (+3)
- Sources/MacParakeetCore/Services/LLMConfigStore.swift (+3)
- Sources/MacParakeetViewModels/LLMSettingsDraft.swift (+35, ~12)
- Sources/MacParakeetViewModels/LLMSettingsViewModel.swift (+50, ~11)
- Sources/MacParakeet/App/AppEnvironment.swift (~2)
- Sources/MacParakeet/Views/Settings/LLMSettingsView.swift (+90, ~65)
- Sources/CLI/Commands/LLMInlineConfig.swift (+10, ~4)
- Tests/MacParakeetTests/Services/LocalCLIExecutorTests.swift (+143)
- Tests/MacParakeetTests/Services/LocalCLILLMClientTests.swift (+117)
- Tests/MacParakeetTests/ViewModels/LLMSettingsViewModelTests.swift (+46)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Beingpax
Copy link
Copy Markdown
Owner

Thanks for the initial work @mitsuhiko! Built on top of your work with some fixes for codex since the main branch had diverged.

@Beingpax Beingpax closed this Apr 10, 2026
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.

2 participants