( recentFiles?: ReadonlySet<string>, )
| 61 | * git branch, recent files) without any model calls. |
| 62 | */ |
| 63 | export async function getVoiceKeyterms( |
| 64 | recentFiles?: ReadonlySet<string>, |
| 65 | ): Promise<string[]> { |
| 66 | const terms = new Set<string>(GLOBAL_KEYTERMS) |
| 67 | |
| 68 | // Project root basename as a single term — users say "claude CLI internal" |
| 69 | // as a phrase, not isolated words. Keeping the whole basename lets the |
| 70 | // STT's keyterm boosting match the phrase regardless of separator. |
| 71 | try { |
| 72 | const projectRoot = getProjectRoot() |
| 73 | if (projectRoot) { |
| 74 | const name = basename(projectRoot) |
| 75 | if (name.length > 2 && name.length <= 50) { |
| 76 | terms.add(name) |
| 77 | } |
| 78 | } |
| 79 | } catch { |
| 80 | // getProjectRoot() may throw if not initialised yet — ignore |
| 81 | } |
| 82 | |
| 83 | // Git branch words (e.g. "feat/voice-keyterms" → "feat", "voice", "keyterms") |
| 84 | try { |
| 85 | const branch = await getBranch() |
| 86 | if (branch) { |
| 87 | for (const word of splitIdentifier(branch)) { |
| 88 | terms.add(word) |
| 89 | } |
| 90 | } |
| 91 | } catch { |
| 92 | // getBranch() may fail if not in a git repo — ignore |
| 93 | } |
| 94 | |
| 95 | // Recent file names — only scan enough to fill remaining slots |
| 96 | if (recentFiles) { |
| 97 | for (const filePath of recentFiles) { |
| 98 | if (terms.size >= MAX_KEYTERMS) break |
| 99 | for (const word of fileNameWords(filePath)) { |
| 100 | terms.add(word) |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | return [...terms].slice(0, MAX_KEYTERMS) |
| 106 | } |
| 107 |
no test coverage detected