Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 135 additions & 6 deletions Sources/MiniWhisperCLI/CLI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@ import Foundation
enum CLI {
static func run(arguments: [String]) async -> Int32 {
guard let command = arguments.first else {
Help.printRoot()
Help.printBareRoot()
return 0
}

if Help.isRootFullHelp(arguments) {
Help.printFull()
return 0
}

let rest = Array(arguments.dropFirst())

switch command {
case "-h", "--help", "help":
case "-h", "--help":
Help.printRoot()
return 0
case "-V", "--version":
Console.out(Version.current)
return 0
case "transcribe":
return await TranscribeCommand.run(arguments: rest)
case "models":
Expand All @@ -37,6 +45,40 @@ enum CLI {
}

enum Help {
static func isRootFullHelp(_ arguments: [String]) -> Bool {
guard arguments.count == 2 else { return false }
return arguments.contains { $0 == "--help" || $0 == "-h" }
&& arguments.contains("--full")
}

static func printBareRoot() {
Console.out(
"""
MiniWhisper CLI
Local audio transcription with Parakeet and Whisper.

Usage:
miniwhispercli <command> [options]

Start here (for AI agents):
miniwhispercli skills get core
miniwhispercli skills get timestamps
miniwhispercli skills list --json

Version-matched guidance. Load core first; use the JSON list for available guides.

Next:
miniwhispercli transcribe <audio>
miniwhispercli models status
miniwhispercli skill install --apply

More:
miniwhispercli skills list
miniwhispercli --help
"""
)
}

static func printRoot() {
Console.out(
"""
Expand All @@ -45,6 +87,13 @@ enum Help {
Usage:
miniwhispercli <command> [options]

Start here (for AI agents):
miniwhispercli skills get core
miniwhispercli skills get timestamps
miniwhispercli skills list --json

Version-matched guidance. Load core first; use the JSON list for available guides.

Common commands:
transcribe <audio> Transcribe audio with Parakeet or Whisper
models status Show local model readiness
Expand All @@ -54,10 +103,14 @@ enum Help {
doctor Check local CLI readiness

Agent integration:
skills get core Print the agent-facing core skill
skills list List bundled runtime guides
skills get core Print the agent-facing core guide
skills get timestamps Print timed-transcript guidance
skill status Show agent skill install status
skill install Install the Claude Code discovery skill
skill uninstall Remove the managed discovery skill
skill install Preview discovery-stub install
skill install --apply Install the discovery stub
skill show Print the bundled discovery stub
skill uninstall Remove the managed discovery stub

Useful examples:
miniwhispercli transcribe audio.wav
Expand All @@ -68,7 +121,80 @@ enum Help {
miniwhispercli transcribe audio.wav --json
miniwhispercli transcribe audio.wav --source system
miniwhispercli models status
miniwhispercli skill install
miniwhispercli skill install --apply

More help:
miniwhispercli <command> --help
miniwhispercli --help --full
miniwhispercli skills list
miniwhispercli skills get core
miniwhispercli skills get timestamps
"""
)
}

static func printFull() {
Console.out(
"""
Usage: miniwhispercli <command> [flags]

MiniWhisper CLI local audio transcription.

Flags:
-h, --help Show help
-V, --version Show version
--full With --help, show this full command reference

Commands:
transcribe <audio> Transcribe audio with Parakeet or Whisper
models Show local model readiness
models status [--json] Show local model readiness
models install <parakeet|whisper> Install or verify a model
paths [--json] Show model, skill, and binary paths
doctor [--json] Check local CLI readiness
skills List bundled runtime guides
skills list [--json] List bundled runtime guides
skills get <name> [--json] Print bundled runtime guidance
skill Show discovery-stub install status
skill status [--json] Show discovery-stub install status
skill install [--dry-run|--apply] Preview or install the managed discovery stub
skill show [--json] Print the bundled discovery stub
skill uninstall [--json] Remove the managed discovery stub
version Show version information

Target options for skill commands:
--target <claude|codex> Agent target (default: claude)
--codex Shortcut for --target codex
--skill-dir <dir> Override the agent skill directory

Transcribe options:
--model <parakeet|whisper> Transcription model (default: parakeet)
--source <microphone|system> Parakeet audio-source normalization
--language <auto|code> Whisper language (default: auto)
--align <none|whisper-token|whisper-dtw>
Whisper word-timing source
--from <time> Start offset; seconds, MM:SS, or HH:MM:SS
--to <time> End offset; seconds, MM:SS, or HH:MM:SS
--offset <time> Start offset as an alternative to --from
--duration <time> Clip duration as an alternative to --to
--streaming Force FluidAudio streaming transcription
--timestamps <none|segment|word> Include clean timestamps in JSON
--word-timestamps Legacy stderr word timestamp dump
--metadata Print transcription metadata to stderr
--format <text|json|srt|vtt> Primary output format (default: text)
-o, --output <file> Write primary output to a file
--json Alias for --format json
--output-json <file> Also write JSON result to a file
--output-srt <file> Also write SRT subtitles to a file
--output-vtt <file> Also write WebVTT subtitles to a file
--quiet Suppress progress messages on stderr

More help:
miniwhispercli <command> --help
miniwhispercli skills get core
miniwhispercli skills get timestamps

Claude skills: \(MiniWhisperPaths.claudeSkills.path)
"""
)
}
Expand All @@ -87,6 +213,8 @@ enum Help {
--source <microphone|system>
Parakeet audio-source normalization (default: microphone)
--language <auto|code> Whisper language (default: auto)
--align <none|whisper-token|whisper-dtw>
Whisper word-timing source; non-none implies --timestamps word
--from <time> Start offset; accepts seconds, MM:SS, or HH:MM:SS
--to <time> End offset; accepts seconds, MM:SS, or HH:MM:SS
--offset <time> Start offset as an alternative to --from
Expand All @@ -110,6 +238,7 @@ enum Help {
miniwhispercli transcribe audio.wav
miniwhispercli transcribe audio.wav --model whisper
miniwhispercli transcribe audio.wav --model whisper --language ja --json
miniwhispercli transcribe audio.wav --model whisper --align whisper-dtw --json
miniwhispercli transcribe audio.wav --format json -o out.json --quiet
miniwhispercli transcribe audio.wav --format srt -o out.srt
miniwhispercli transcribe audio.wav --from 01:20 --duration 50
Expand Down
9 changes: 6 additions & 3 deletions Sources/MiniWhisperCLI/DoctorCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,14 @@ enum DoctorCommand {
)
)

let claudeSkillDirExists = FileManager.default.fileExists(atPath: MiniWhisperPaths.claudeSkills.path)
checks.append(
DoctorCheck(
name: "Skill directory",
status: FileManager.default.fileExists(atPath: MiniWhisperPaths.documentSkills.path) ? "ok" : "warning",
detail: MiniWhisperPaths.documentSkills.path
name: "Claude skill directory",
status: claudeSkillDirExists ? "ok" : "warning",
detail: claudeSkillDirExists
? MiniWhisperPaths.claudeSkills.path
: "Missing. `miniwhispercli skill install --apply` will create it."
)
)

Expand Down
2 changes: 1 addition & 1 deletion Sources/MiniWhisperCLI/ModelsCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ enum ModelsCommand {
let rest = Array(arguments.dropFirst())

switch subcommand {
case "-h", "--help", "help":
case "-h", "--help":
printHelp()
return 0
case "status":
Expand Down
35 changes: 21 additions & 14 deletions Sources/MiniWhisperCLI/Paths.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,25 @@ enum MiniWhisperPaths {
whisperModels.appendingPathComponent("ggml-silero-v6.2.0.bin")
}

static var documentSkills: URL {
static var claudeSkills: URL {
FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent("Documents", isDirectory: true)
.appendingPathComponent("MiniWhisper", isDirectory: true)
.appendingPathComponent(".claude", isDirectory: true)
.appendingPathComponent("skills", isDirectory: true)
}

static var coreSkillCopy: URL {
documentSkills.appendingPathComponent(CoreSkill.directoryName, isDirectory: true)
}

static var claudeSkills: URL {
static var codexSkills: URL {
FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent(".claude", isDirectory: true)
.appendingPathComponent(".codex", isDirectory: true)
.appendingPathComponent("skills", isDirectory: true)
}

static var coreSkillClaudeInstall: URL {
claudeSkills.appendingPathComponent(CoreSkill.directoryName, isDirectory: true)
}

static var coreSkillCodexInstall: URL {
codexSkills.appendingPathComponent(CoreSkill.directoryName, isDirectory: true)
}
}

struct PathsOutput: Encodable {
Expand All @@ -70,8 +69,10 @@ struct PathsOutput: Encodable {
let whisperModelInstalled: Bool
let whisperVADModel: String
let whisperVADModelInstalled: Bool
let documentSkills: String
let claudeSkills: String
let claudeSkill: String
let codexSkills: String
let codexSkill: String

enum CodingKeys: String, CodingKey {
case executable
Expand All @@ -83,8 +84,10 @@ struct PathsOutput: Encodable {
case whisperModelInstalled = "whisper_model_installed"
case whisperVADModel = "whisper_vad_model"
case whisperVADModelInstalled = "whisper_vad_model_installed"
case documentSkills = "document_skills"
case claudeSkills = "claude_skills"
case claudeSkill = "claude_skill"
case codexSkills = "codex_skills"
case codexSkill = "codex_skill"
}
}

Expand Down Expand Up @@ -112,8 +115,10 @@ enum PathsCommand {
whisperModelInstalled: FileManager.default.fileExists(atPath: MiniWhisperPaths.whisperModel.path),
whisperVADModel: MiniWhisperPaths.whisperVADModel.path,
whisperVADModelInstalled: FileManager.default.fileExists(atPath: MiniWhisperPaths.whisperVADModel.path),
documentSkills: MiniWhisperPaths.documentSkills.path,
claudeSkills: MiniWhisperPaths.claudeSkills.path
claudeSkills: MiniWhisperPaths.claudeSkills.path,
claudeSkill: MiniWhisperPaths.coreSkillClaudeInstall.path,
codexSkills: MiniWhisperPaths.codexSkills.path,
codexSkill: MiniWhisperPaths.coreSkillCodexInstall.path
)

if emitJSON {
Expand All @@ -136,8 +141,10 @@ enum PathsCommand {
MiniWhisper Whisper models: \(output.miniWhisperWhisperModels)
Whisper model: \(status(output.whisperModelInstalled)) \(output.whisperModel)
Whisper VAD model: \(status(output.whisperVADModelInstalled)) \(output.whisperVADModel)
Document skills: \(output.documentSkills)
Claude skills: \(output.claudeSkills)
Claude discovery skill: \(output.claudeSkill)
Codex skills: \(output.codexSkills)
Codex discovery skill: \(output.codexSkill)
"""
)
return 0
Expand Down
Loading
Loading