Skip to content

Add AssemblyAI studio flow and align versions#46

Merged
nmbrthirteen merged 4 commits into
nmbrthirteen:mainfrom
teethatkamsai:codex/uninstall-cleans-all
Jul 6, 2026
Merged

Add AssemblyAI studio flow and align versions#46
nmbrthirteen merged 4 commits into
nmbrthirteen:mainfrom
teethatkamsai:codex/uninstall-cleans-all

Conversation

@teethatkamsai

@teethatkamsai teethatkamsai commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add AssemblyAI as a selectable transcription engine in Studio and CLI while keeping Whisper as the default path.
  • Save AssemblyAI API key through Secrets, use saved keys from the backend, and avoid FFmpeg-only audio extraction for AssemblyAI uploads.
  • Fix Studio library/new episode and file browse interactions, plus refresh-time cleanup for cached uploads and transcript state.
  • Add shared version helpers so Go CLI, Python backend, Studio/MCP, and package metadata stay aligned with the GitHub release tag.
  • Include the current multi-format clip work from upstream/main, including horizontal format plumbing and caption layout updates.

Validation

  • npm.cmd run build
  • python -m unittest tests.test_transcription_engine tests.test_transcript_cache_engine
  • python -c "import sys; sys.path.insert(0, 'backend'); import version; print(version.VERSION)"
  • go test ./internal/update

Summary by CodeRabbit

  • New Features

    • Added AssemblyAI as a transcription option alongside existing engines.
    • Introduced engine selection in transcription workflows and improved speaker/diarization handling.
    • Added clip format options for vertical, horizontal, and square exports.
    • Added a way to clear the current session state from the app.
  • Bug Fixes

    • Transcription and transcript caches now keep results separate by engine.
    • Improved version reporting across the app and release build.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@teethatkamsai, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 43 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 764929c0-0d44-4d5b-9f95-82cbc3f5799e

📥 Commits

Reviewing files that changed from the base of the PR and between 721ff11 and 11e9142.

📒 Files selected for processing (7)
  • backend/cli.py
  • backend/clip_studio.py
  • backend/services/engines.py
  • backend/services/transcript_packer.py
  • backend/services/transcription.py
  • cli/main.go
  • src/handlers/transcribe.handler.ts
📝 Walkthrough

Walkthrough

This PR adds AssemblyAI as an alternative transcription engine alongside Whisper, threading engine selection through backend services, CLI, MCP server tools, web server endpoints, and client UI. It also centralizes version reporting, adds a session-cache-clear endpoint, unifies clip format typing, and includes several unrelated smaller fixes.

Changes

AssemblyAI Transcription Engine Support

Layer / File(s) Summary
AssemblyAI backend implementation
backend/services/transcription.py, tests/test_transcription_engine.py
Adds region-aware HTTP client, upload/polling job lifecycle, response normalization to words/segments/speakers, and engine parameter to transcribe_file; updates speaker/face attachment logic; adds unit test for explicit AssemblyAI selection.
Engine-aware cache suffix (Python)
backend/services/transcript_packer.py, backend/main.py, tests/test_transcript_cache_engine.py
Extracts public engine_cache_suffix() mapping engine names to cache namespaces including -assemblyai; used in cache hash computation and covered by new tests.
Backend CLI/server plumbing
backend/cli.py, backend/clip_studio.py, backend/main.py, backend/services/env_settings.py
Adds --engine/--assemblyai-api-key CLI options, environment variable injection for subprocesses, and a new ASSEMBLYAI_API_KEY secret setting.
Engine-aware transcript cache and MCP schemas (TS)
src/services/transcript-cache.ts, src/handlers/transcribe.handler.ts, src/server.ts, tests
Adds engine-suffixed cache hashing and threads optional engine through transcribe_podcast/transcribe_start tool schemas and cache get/set/getPackedMarkdown.
Web server transcription endpoint
src/ui/web-server.ts
/api/transcribe accepts engine/assemblyai_api_key and uses engine-aware cache lookups.
Episode workspace engine UI
src/ui/client/EpisodeWorkspace.jsx
Adds Whisper/AssemblyAI engine selector, API key input, and stricter auto-transcription gating.

Estimated code review effort: 4 (Complex) | ~60 minutes

Version Centralization

Layer / File(s) Summary
Version resolution modules
backend/version.py, src/version.ts, backend/cli.py, backend/main.py, src/server.ts
Adds podcli_version()/podcliVersion() resolving version from env var or package.json, replacing hardcoded strings.
Version bump and release wiring
cli/main.go, cli/main_test.go, package.json, .github/workflows/release.yml
Bumps CLI/package version to 2.2.1, sets PODCLI_VERSION env var at startup, and syncs studio npm version to the release tag.

Session Cache Clear Endpoint

Layer / File(s) Summary
Session clear endpoint and trigger
src/ui/web-server.ts, src/ui/client/Layout.tsx
Adds POST /api/session-cache/clear that blocks (409) on active jobs or clears session/UI state (200); triggered on client mount.

Format Type Unification

Layer / File(s) Summary
Format type propagation
src/models/index.ts, src/server.ts, src/services/clips-history.ts, src/ui/web-server.ts
Introduces Format union type and replaces format?: string across clip/UI/history interfaces and tool schemas.

Miscellaneous Fixes

Layer / File(s) Summary
Error logging
backend/services/content_generator.py, backend/utils/proc.py
Adds stderr warnings for AI CLI exceptions/cleanup failures and includes truncated stderr in subprocess failure logs.
Installer changes
install.ps1
Adds PATH-change broadcast, reworks uninstall PATH pruning/-Purge deletion logic, switches checksum retrieval method.
ContentStudio UX
src/ui/client/ContentStudio.tsx
Adds unified busy state, localStorage persistence, custom requests, and per-field regeneration.
Upload/preview handling
src/ui/client/EpisodeWorkspace.jsx
Adds hidden file input upload path and resets preview/clip state on video switch/download completion.
Small UI fixes
remotion/src/components/SubtleCaptions.tsx, src/ui/client/ConfigPage.tsx, src/ui/client/StudioHome.tsx, src/ui/public/css/styles.css
Fixes caption font scaling, config placeholder, empty-state link, and removes unused CSS variable.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant TranscribeFile
  participant AssemblyAI
  participant AttachSpeakersFaces

  CLI->>TranscribeFile: transcribe_file(engine="assemblyai")
  TranscribeFile->>AssemblyAI: upload media & start transcription job
  AssemblyAI-->>TranscribeFile: poll status until completed
  TranscribeFile->>AttachSpeakersFaces: merge with diarization disabled
  AttachSpeakersFaces-->>TranscribeFile: words/segments/speakers
  TranscribeFile-->>CLI: result tagged engine="assemblyai"
Loading
sequenceDiagram
  participant Client
  participant WebServer
  participant TranscriptCache
  participant PythonExecutor

  Client->>WebServer: POST /api/transcribe (engine, api_key)
  WebServer->>TranscriptCache: get(filePath, engine)
  alt cache hit
    TranscriptCache-->>WebServer: cached transcript
    WebServer-->>Client: cached: true
  else cache miss
    WebServer->>PythonExecutor: execute transcribe(engine, api_key)
    PythonExecutor-->>WebServer: transcript result
    WebServer->>TranscriptCache: set(filePath, result, engine)
    WebServer-->>Client: transcript result
  end
Loading

Possibly related PRs

  • nmbrthirteen/podcli#16: Both PRs modify engine-based cache namespacing in backend/services/transcript_packer.py, extending the suffix mapping introduced there to include AssemblyAI.
  • nmbrthirteen/podcli#30: Both PRs modify backend/services/transcription.py's _attach_speakers_and_faces and engine-selection logic in transcribe_file.
  • nmbrthirteen/podcli#37: Both PRs modify generate_custom_content(...) in backend/services/content_generator.py, adjusting exception handling and warning behavior.

Suggested labels: enhancement, feature, backend, frontend

Suggested reviewers: nmbrthirteen

🐰 A new engine hums beside the old,
AssemblyAI speaks, transcripts told,
Versions sync from tag to file,
Cache keys sorted, format style,
Hop along, the code review's not cold!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main changes: adding AssemblyAI studio support and aligning version sources across components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (3)
remotion/src/components/SubtleCaptions.tsx (1)

88-117: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Second caption line's font size isn't scaled — inconsistent sizing between line1/line2.

text1's span was updated to style.fontSize * s (Line 91), but the text2 span at Line 106 still uses the unscaled style.fontSize. On any video height other than the reference height, the second caption line will render at a different (unscaled) size than the first line, breaking the height-responsive scaling this PR introduces.

🐛 Proposed fix
       {text2 && (
         <span
           style={{
             fontFamily: style.fontFamily,
-            fontSize: style.fontSize,
+            fontSize: style.fontSize * s,
             fontWeight: 400,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@remotion/src/components/SubtleCaptions.tsx` around lines 88 - 117, The second
caption line in SubtleCaptions is not using the same height-based scaling as the
first line, causing inconsistent font sizes between text1 and text2. Update the
text2 span in SubtleCaptions to use the same scaled font size calculation as
text1 (the style.fontSize multiplied by s), keeping the sizing logic consistent
across both caption lines.
src/server.ts (1)

1131-1199: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Add format to clip_history duplicate checks
findDuplicate compares format, but this tool never accepts or forwards it, so horizontal/square clips will be checked as "vertical" and can slip through.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server.ts` around lines 1131 - 1199, The clip history duplicate-check
path is missing the format field, so `history.findDuplicate` is being called
without the same `format` value it compares against. Update the tool schema and
the `async` handler in `server.ts` to accept and forward `format` alongside
`action`, `source_video`, `start_second`, `end_second`, `caption_style`, and
`crop_strategy`, and make sure the `"check"` branch passes that value into
`history.findDuplicate` instead of relying on a hardcoded default.
backend/main.py (1)

77-111: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use the resolved engine for the packed cache key. When engine is omitted, transcribe_file falls back to PODCLI_ENGINE, but engine_cache_suffix(engine) still sees None here, so packed transcripts can be written under the wrong namespace. Derive the suffix from the engine actually used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 77 - 111, The packed transcript cache key is
using the requested engine value instead of the engine actually used by
transcribe_file, so omitted engine requests can be stored under the wrong
namespace. Update the cache-key/suffix logic near the transcribe_file call and
engine_cache_suffix usage to derive the resolved engine from the effective
PODCLI_ENGINE value (or otherwise capture the fallback engine before packing).
Keep the environment restore behavior intact, but ensure the packing path uses
the same engine resolution as transcribe_file.
🧹 Nitpick comments (12)
backend/requirements.txt (1)

23-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider pinning yt-dlp to an exact/capped version.

yt-dlp ships frequent releases (site-breakage fixes, occasional CLI/API changes). An unbounded >= floor can silently pull in a version with behavior changes on rebuilds. Consider pinning exactly or capping with <.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/requirements.txt` at line 23, The yt-dlp dependency is only bounded
with a minimum version, which can allow unexpected behavior changes on future
rebuilds. Update the dependency specification in requirements.txt to either pin
yt-dlp to an exact known-good version or add an upper bound with the existing
requirement entry so installs remain stable.
src/ui/public/css/styles.css (1)

9-11: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate CSS variable value under two names.

--border-hover and --border-h both equal rgba(255, 255, 255, 0.12). Consider consolidating to a single variable name to avoid future drift where one gets updated and not the other.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/public/css/styles.css` around lines 9 - 11, The CSS custom properties
in styles.css define the same value twice under --border-hover and --border-h,
so consolidate them to a single variable name and update any references
accordingly. Keep the shared value in one place near the root variables and use
the existing semantic name consistently throughout the stylesheet to avoid
future drift.
backend/utils/proc.py (1)

80-92: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reasonable quieting of expected non-zero exits.

Gating the warning+raise behind check avoids noisy warnings for callers that intentionally use check=False. Minor: the new debug branch drops the truncated stderr that the warning branch logs, which could reduce debuggability for check=False failures.

♻️ Optional: include truncated stderr in the debug log too
-        log.debug("proc.nonzero tool=%s rc=%d duration=%.2fs", tool, result.returncode, duration)
+        log.debug(
+            "proc.nonzero tool=%s rc=%d duration=%.2fs stderr=%s",
+            tool, result.returncode, duration, (result.stderr or "")[-400:].strip(),
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/utils/proc.py` around lines 80 - 92, The proc execution failure
handling in the returncode branch currently omits the truncated stderr from the
check=False debug path, which reduces debuggability for expected non-zero exits.
Update the proc error logging in the conditional around result.returncode, using
the existing tool/result/duration variables, so the log.debug for non-check
failures includes the same truncated stderr content already used in the warning
path while keeping the warning+raise behavior gated by check.
src/ui/client/Layout.tsx (1)

33-37: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Non-2xx responses aren't caught by the warning.

fetch resolves (doesn't reject) on HTTP error statuses, so a 4xx/5xx from /api/session-cache/clear won't trigger console.warn. Since this is a best-effort cache clear, low priority, but worth checking res.ok for accurate diagnostics.

🔧 Optional fix
   useEffect(() => {
-    fetch("/api/session-cache/clear", { method: "POST" }).catch((err: unknown) => {
-      console.warn("Failed to clear session cache", err);
-    });
+    fetch("/api/session-cache/clear", { method: "POST" })
+      .then((res) => {
+        if (!res.ok) console.warn("Failed to clear session cache", res.status);
+      })
+      .catch((err: unknown) => {
+        console.warn("Failed to clear session cache", err);
+      });
   }, []);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/client/Layout.tsx` around lines 33 - 37, The session-cache clear call
in Layout.tsx only warns on rejected fetches, so HTTP 4xx/5xx responses from
/api/session-cache/clear are missed. Update the useEffect fetch flow to inspect
the Response from fetch and log the warning when it is not ok, keeping the
existing console.warn path in Layout.tsx accurate for best-effort cache
clearing.
backend/clip_studio.py (1)

204-205: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

API key exposed via CLI argument.

--assemblyai-api-key places the secret in process arguments (visible via ps/shell history/process listings). The env var ASSEMBLYAI_API_KEY already works without this flag; consider dropping the CLI flag or documenting the risk, since Secrets are otherwise meant to be managed via the backend settings flow per the PR objectives.

Also applies to: 246-247

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/clip_studio.py` around lines 204 - 205, The CLI parser in
clip_studio.py exposes the AssemblyAI secret via the --assemblyai-api-key
argument, which should be removed or avoided in favor of the existing
ASSEMBLYAI_API_KEY environment variable and backend settings flow. Update the
argument handling in the parser setup (the ap.add_argument calls for --engine
and --assemblyai-api-key, plus any later use around the referenced assemblyai
setup) so the API key is not accepted from process arguments; if the flag must
remain, clearly document the risk and ensure the env var path is preferred.
src/models/index.ts (1)

74-74: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Format type alias is defined but unused — format fields stay typed as string.

Using the new Format union instead of string on these fields would catch typos/invalid values (e.g. "squre") at compile time across handlers, matching the zod enums already used at the API boundary.

♻️ Example
-export interface ClipResult {
-  ...
-  format?: string;
+export interface ClipResult {
+  ...
+  format?: Format;

(repeat for UIState.settings.format, CreateClipInput.format, BatchClipSpec.format, BatchClipsInput.format, BatchClipsResult.results[].format, ClipHistoryEntry.format)

Also applies to: 90-90, 121-121, 137-137, 152-152, 165-165, 188-188, 237-237

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/models/index.ts` at line 74, The Format union in models/index.ts is
defined but not used, so several format fields still accept plain string values.
Update the relevant type definitions and interfaces (including
UIState.settings.format, CreateClipInput.format, BatchClipSpec.format,
BatchClipsInput.format, BatchClipsResult.results[].format, and
ClipHistoryEntry.format) to use the Format alias instead of string, so the
shared format type is enforced consistently and invalid values are caught at
compile time.
src/ui/web-server.ts (1)

382-493: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Cache/session clear doesn't guard against in-flight jobs.

/api/session-cache/clear deletes upload/transcript/packed files and resets uiState unconditionally. If a transcribe/create_clip/batch_clips job is still running and referencing those same on-disk files, clearing mid-job can cause it to fail or produce inconsistent output. Consider checking the jobs map for any running job before proceeding (or rejecting/warning the caller).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/web-server.ts` around lines 382 - 493, The session cache clear route
currently deletes files and resets state without checking for active work, which
can break in-flight processing. Update the /api/session-cache/clear handler in
web-server.ts to inspect the jobs map before calling clearCacheFiles and
clearEpisodeSessionState, and reject or warn if any transcribe, create_clip, or
batch_clips job is still running. Use the existing job/status symbols in the
route logic so the guard is applied before any files or uiState are modified.
backend/main.py (1)

28-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate engine-suffix mapping vs transcript_packer._engine_cache_suffix.

This engine_cache_suffix() reimplements the same whisper/assemblyai alias mapping already in backend/services/transcript_packer.py::_engine_cache_suffix. Two independent copies of this mapping are already diverging in signature (one reads env, one takes a param); adding a new engine alias later requires remembering to update both.

♻️ Proposed consolidation
-def engine_cache_suffix(engine):
-    value = (engine or "").strip().lower()
-    if value in ("whispercpp", "whisper-cpp", "whisper.cpp", "cpp"):
-        return "-whispercpp"
-    if value in ("assemblyai", "assembly-ai", "aai"):
-        return "-assemblyai"
-    return ""
+from services.transcript_packer import engine_cache_suffix  # exported, param-based
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.py` around lines 28 - 37, The engine alias-to-suffix mapping is
duplicated between engine_cache_suffix and
backend/services/transcript_packer.py::_engine_cache_suffix, which will drift
over time. Consolidate the alias logic into a single shared helper (or have
engine_cache_suffix delegate to the existing transcript_packer function) and
update callers so there is one source of truth for whisper/assemblyai suffix
resolution. Keep the unique mapping behavior centralized in the existing
_engine_cache_suffix or a clearly named shared utility referenced by
engine_cache_suffix.
backend/services/content_generator.py (1)

224-249: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log exceptions before swallowing them.

Both the per-engine attempt (except Exception: continue) and the temp-file cleanup (except Exception: pass) silently discard the error. If all AI CLI candidates fail for a real reason (auth, network, malformed CLI output), the caller only sees a generic "No AI CLI available" message, hiding the actual cause and making this hard to debug in production.

♻️ Proposed fix to log before swallowing
             try:
                 cr = _run_ai_command(
                     cli_path=cli_path,
                     engine=engine,
                     prompt=prompt[:4000] if engine == "codex" else prompt,
                     prompt_file=prompt_file,
                     project_dir=project_dir,
                     timeout=120,
                 )
-            except Exception:
+            except Exception as e:
+                print(f"Warning: {label} failed: {e}", file=sys.stderr)
                 continue
     finally:
         try:
             os.unlink(prompt_file)
-        except Exception:
-            pass
+        except Exception as e:
+            print(f"Warning: failed to remove prompt file {prompt_file}: {e}", file=sys.stderr)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/services/content_generator.py` around lines 224 - 249, The AI CLI
fallback in the content generation flow is swallowing failures, so log the
exception before continuing or ignoring it. In the candidate loop inside the
content generator routine that calls _run_ai_command, catch the exception, emit
a warning/error with the engine label and exception details, then continue; also
log any exception raised during the prompt_file cleanup in the finally block
instead of silently passing. Keep the existing return behavior unchanged, but
make sure the logs preserve the real failure cause for debugging.

Source: Linters/SAST tools

src/ui/client/EpisodeWorkspace.jsx (1)

965-993: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Stale preview after video download.

On successful download, transcript/suggestions/results/energy state are reset, but previewSrc and activeClipIdx are not. If a rendered-clip preview or an active clip selection was set before downloading a new URL, the preview panel can keep showing the old clip/video after the new one replaces videoPath.

🔧 Suggested addition
           setResults([]);
           setEnergyData({});
           autoTranscribeRef.current = '';
+          setActiveClipIdx(null);
+          setPreviewSrc(null);
           setDownloadingVideo(false);
           setDownloadJobId(null);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/client/EpisodeWorkspace.jsx` around lines 965 - 993, The download
success handler in EpisodeWorkspace.jsx leaves stale preview state behind when a
new video replaces the old one. In the useEffect that watches
downloadStream.status, update the success path to also clear or reset previewSrc
and activeClipIdx along with the existing transcript/results resets, so the
preview panel can’t keep showing the previous clip after setVideoPath runs.
src/ui/client/ContentStudio.tsx (1)

104-197: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Concurrent generate/regenerate/custom actions can clobber saved state.

busy, regenBusy, and customBusy gate their own buttons independently, so a user can kick off generate(), regenerate(), and askCustom() at the same time. Each writes result/persist() on completion, so whichever resolves last silently overwrites the others' output in state and localStorage.

🔧 Suggested cross-guarding
-              <button className="btn btn-primary btn-sm" onClick={generate} disabled={busy || !transcript.trim()}>
+              <button className="btn btn-primary btn-sm" onClick={generate} disabled={busy || regenBusy || customBusy || !transcript.trim()}>
-              onClick={askCustom}
-              disabled={customBusy || !customReq.trim()}
+              onClick={askCustom}
+              disabled={busy || regenBusy || customBusy || !customReq.trim()}
-      onClick={() => { setRegenField(regenField === field ? null : field); setRegenNote(""); }}
-      disabled={regenBusy}
+      onClick={() => { setRegenField(regenField === field ? null : field); setRegenNote(""); }}
+      disabled={busy || regenBusy || customBusy}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/client/ContentStudio.tsx` around lines 104 - 197, Concurrent generate,
regenerate, and custom actions can overwrite each other because generate(),
regenerate(), and askCustom() update result/persist independently while only
guarding their own busy flags. Add cross-guarding in ContentStudio so these
actions are mutually exclusive (for example by sharing a single in-flight state
or blocking each action when any other is running), and ensure generate(),
regenerate(), and askCustom() all respect that shared lock before writing result
or calling persist().
src/ui/client/ClipDetail.tsx (1)

141-169: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

newFrame duplicates the logic in ThumbnailStudio.tsx.

This handler is nearly line-for-line identical to newFrame newly added in src/ui/client/ThumbnailStudio.tsx (fetch options if empty → cycle by index relative to selFrame/frameIdx → render). Consider extracting a shared hook to avoid divergent bug fixes later.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/client/ClipDetail.tsx` around lines 141 - 169, The newFrame handler in
ClipDetail duplicates the same option-fetching, frame-cycling, and render flow
used in ThumbnailStudio, so centralize this shared behavior into a reusable hook
or helper instead of keeping two near-identical implementations. Move the common
logic behind a symbol both screens can call, and keep ClipDetail’s newFrame
focused on passing clip-specific state and callbacks so future bug fixes only
need to be made once.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/cli.py`:
- Around line 364-367: The cmd_studio subprocess setup is leaking the AssemblyAI
secret by appending args.assemblyai_api_key to the argv list; update cmd_studio
to follow the same safer pattern used by cmd_process/transcribe_file by placing
the value in an env dict as ASSEMBLYAI_API_KEY and passing that env into
subprocess.run. Also adjust clip_studio.py to read the key from the environment
instead of expecting a --assemblyai-api-key flag, and remove the argv handling
for that option.

In `@backend/services/transcription.py`:
- Around line 251-263: The AssemblyAI speaker summary in _assemblyai_result
currently only populates the speakers mapping with label, but it needs to match
the shared speakers.speakers[SPEAKER_XX] structure. Update the speaker entries
inside the speakers object to include total_time and segments alongside label,
using the existing speaker_segments data and computed durations so downstream
ranking/reporting has the same shape as other transcription paths.

In `@install.ps1`:
- Around line 35-49: Send-EnvironmentPathChange currently throws on
SendMessageTimeout failure, which makes install/uninstall abort even after the
PATH update work is already done. Change the failure path in
Send-EnvironmentPathChange to emit a warning or non-terminating error instead of
throwing, and keep the install/uninstall callers resilient so a notification
broadcast issue does not fail the overall script. Use the
Send-EnvironmentPathChange helper and its install/uninstall call sites as the
main places to update.

In `@src/version.ts`:
- Around line 8-13: The version fallback in getVersion() is resolving
package.json from process.cwd(), which can miss the real package when the
process starts elsewhere. Update the logic to resolve package.json relative to
the module location in src/version.ts (using the existing
getVersion/readFileSync/resolve flow), so the fallback reliably reads the actual
package version instead of returning 0.0.0-dev.

---

Outside diff comments:
In `@backend/main.py`:
- Around line 77-111: The packed transcript cache key is using the requested
engine value instead of the engine actually used by transcribe_file, so omitted
engine requests can be stored under the wrong namespace. Update the
cache-key/suffix logic near the transcribe_file call and engine_cache_suffix
usage to derive the resolved engine from the effective PODCLI_ENGINE value (or
otherwise capture the fallback engine before packing). Keep the environment
restore behavior intact, but ensure the packing path uses the same engine
resolution as transcribe_file.

In `@remotion/src/components/SubtleCaptions.tsx`:
- Around line 88-117: The second caption line in SubtleCaptions is not using the
same height-based scaling as the first line, causing inconsistent font sizes
between text1 and text2. Update the text2 span in SubtleCaptions to use the same
scaled font size calculation as text1 (the style.fontSize multiplied by s),
keeping the sizing logic consistent across both caption lines.

In `@src/server.ts`:
- Around line 1131-1199: The clip history duplicate-check path is missing the
format field, so `history.findDuplicate` is being called without the same
`format` value it compares against. Update the tool schema and the `async`
handler in `server.ts` to accept and forward `format` alongside `action`,
`source_video`, `start_second`, `end_second`, `caption_style`, and
`crop_strategy`, and make sure the `"check"` branch passes that value into
`history.findDuplicate` instead of relying on a hardcoded default.

---

Nitpick comments:
In `@backend/clip_studio.py`:
- Around line 204-205: The CLI parser in clip_studio.py exposes the AssemblyAI
secret via the --assemblyai-api-key argument, which should be removed or avoided
in favor of the existing ASSEMBLYAI_API_KEY environment variable and backend
settings flow. Update the argument handling in the parser setup (the
ap.add_argument calls for --engine and --assemblyai-api-key, plus any later use
around the referenced assemblyai setup) so the API key is not accepted from
process arguments; if the flag must remain, clearly document the risk and ensure
the env var path is preferred.

In `@backend/main.py`:
- Around line 28-37: The engine alias-to-suffix mapping is duplicated between
engine_cache_suffix and
backend/services/transcript_packer.py::_engine_cache_suffix, which will drift
over time. Consolidate the alias logic into a single shared helper (or have
engine_cache_suffix delegate to the existing transcript_packer function) and
update callers so there is one source of truth for whisper/assemblyai suffix
resolution. Keep the unique mapping behavior centralized in the existing
_engine_cache_suffix or a clearly named shared utility referenced by
engine_cache_suffix.

In `@backend/requirements.txt`:
- Line 23: The yt-dlp dependency is only bounded with a minimum version, which
can allow unexpected behavior changes on future rebuilds. Update the dependency
specification in requirements.txt to either pin yt-dlp to an exact known-good
version or add an upper bound with the existing requirement entry so installs
remain stable.

In `@backend/services/content_generator.py`:
- Around line 224-249: The AI CLI fallback in the content generation flow is
swallowing failures, so log the exception before continuing or ignoring it. In
the candidate loop inside the content generator routine that calls
_run_ai_command, catch the exception, emit a warning/error with the engine label
and exception details, then continue; also log any exception raised during the
prompt_file cleanup in the finally block instead of silently passing. Keep the
existing return behavior unchanged, but make sure the logs preserve the real
failure cause for debugging.

In `@backend/utils/proc.py`:
- Around line 80-92: The proc execution failure handling in the returncode
branch currently omits the truncated stderr from the check=False debug path,
which reduces debuggability for expected non-zero exits. Update the proc error
logging in the conditional around result.returncode, using the existing
tool/result/duration variables, so the log.debug for non-check failures includes
the same truncated stderr content already used in the warning path while keeping
the warning+raise behavior gated by check.

In `@src/models/index.ts`:
- Line 74: The Format union in models/index.ts is defined but not used, so
several format fields still accept plain string values. Update the relevant type
definitions and interfaces (including UIState.settings.format,
CreateClipInput.format, BatchClipSpec.format, BatchClipsInput.format,
BatchClipsResult.results[].format, and ClipHistoryEntry.format) to use the
Format alias instead of string, so the shared format type is enforced
consistently and invalid values are caught at compile time.

In `@src/ui/client/ClipDetail.tsx`:
- Around line 141-169: The newFrame handler in ClipDetail duplicates the same
option-fetching, frame-cycling, and render flow used in ThumbnailStudio, so
centralize this shared behavior into a reusable hook or helper instead of
keeping two near-identical implementations. Move the common logic behind a
symbol both screens can call, and keep ClipDetail’s newFrame focused on passing
clip-specific state and callbacks so future bug fixes only need to be made once.

In `@src/ui/client/ContentStudio.tsx`:
- Around line 104-197: Concurrent generate, regenerate, and custom actions can
overwrite each other because generate(), regenerate(), and askCustom() update
result/persist independently while only guarding their own busy flags. Add
cross-guarding in ContentStudio so these actions are mutually exclusive (for
example by sharing a single in-flight state or blocking each action when any
other is running), and ensure generate(), regenerate(), and askCustom() all
respect that shared lock before writing result or calling persist().

In `@src/ui/client/EpisodeWorkspace.jsx`:
- Around line 965-993: The download success handler in EpisodeWorkspace.jsx
leaves stale preview state behind when a new video replaces the old one. In the
useEffect that watches downloadStream.status, update the success path to also
clear or reset previewSrc and activeClipIdx along with the existing
transcript/results resets, so the preview panel can’t keep showing the previous
clip after setVideoPath runs.

In `@src/ui/client/Layout.tsx`:
- Around line 33-37: The session-cache clear call in Layout.tsx only warns on
rejected fetches, so HTTP 4xx/5xx responses from /api/session-cache/clear are
missed. Update the useEffect fetch flow to inspect the Response from fetch and
log the warning when it is not ok, keeping the existing console.warn path in
Layout.tsx accurate for best-effort cache clearing.

In `@src/ui/public/css/styles.css`:
- Around line 9-11: The CSS custom properties in styles.css define the same
value twice under --border-hover and --border-h, so consolidate them to a single
variable name and update any references accordingly. Keep the shared value in
one place near the root variables and use the existing semantic name
consistently throughout the stylesheet to avoid future drift.

In `@src/ui/web-server.ts`:
- Around line 382-493: The session cache clear route currently deletes files and
resets state without checking for active work, which can break in-flight
processing. Update the /api/session-cache/clear handler in web-server.ts to
inspect the jobs map before calling clearCacheFiles and
clearEpisodeSessionState, and reject or warn if any transcribe, create_clip, or
batch_clips job is still running. Use the existing job/status symbols in the
route logic so the guard is applied before any files or uiState are modified.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 27fd1799-1b9f-4be2-8758-4df3a6ff74b9

📥 Commits

Reviewing files that changed from the base of the PR and between f9c6f30 and 5e5bc27.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (54)
  • .github/workflows/release.yml
  • README.md
  • backend/cli.py
  • backend/clip_studio.py
  • backend/main.py
  • backend/presets.py
  • backend/requirements-runtime.txt
  • backend/requirements.txt
  • backend/services/clip_generator.py
  • backend/services/content_generator.py
  • backend/services/env_settings.py
  • backend/services/formats.py
  • backend/services/transcript_packer.py
  • backend/services/transcription.py
  • backend/services/video_processor.py
  • backend/utils/proc.py
  • backend/version.py
  • cli/internal/provision/provision.go
  • cli/internal/update/update.go
  • cli/main.go
  • cli/main_test.go
  • cli/uninstall_test.go
  • install.ps1
  • install.sh
  • package.json
  • plans/horizontal-clips.md
  • remotion/src/components/BrandedCaptions.tsx
  • remotion/src/components/HormoziCaptions.tsx
  • remotion/src/components/KaraokeCaptions.tsx
  • remotion/src/components/SubtleCaptions.tsx
  • remotion/src/types.ts
  • src/handlers/batch-clips.handler.ts
  • src/handlers/create-clip.handler.ts
  • src/models/index.ts
  • src/server.ts
  • src/services/clips-history.ts
  • src/services/transcript-cache.ts
  • src/ui/client/AnalyticsPage.tsx
  • src/ui/client/ClipDetail.tsx
  • src/ui/client/ConfigPage.tsx
  • src/ui/client/ContentStudio.tsx
  • src/ui/client/EpisodeWorkspace.jsx
  • src/ui/client/Layout.tsx
  • src/ui/client/ReframeEditor.tsx
  • src/ui/client/StudioHome.tsx
  • src/ui/client/ThumbnailStudio.tsx
  • src/ui/client/ThumbnailTemplate.tsx
  • src/ui/client/icons.tsx
  • src/ui/client/lib.ts
  • src/ui/public/css/styles.css
  • src/ui/web-server.ts
  • src/version.ts
  • tests/test_transcript_cache_engine.py
  • tests/test_transcription_engine.py

Comment thread backend/cli.py Outdated
Comment thread backend/services/transcription.py
Comment thread install.ps1
Comment thread src/version.ts
@teethatkamsai

Copy link
Copy Markdown
Contributor Author

Verified the attached findings against current code and fixed the still-valid ones.

Fixed:

  • Removed AssemblyAI key forwarding via clip_studio.py argv; cmd_studio now passes it through ASSEMBLYAI_API_KEY.
  • Removed --assemblyai-api-key from clip_studio.py.
  • AssemblyAI speaker summary now includes label, total_time, and segments.
  • Packed transcript cache now uses the actual resolved transcription engine.
  • Shared the engine cache suffix logic through transcript_packer.
  • Installer PATH broadcast failure now warns instead of aborting install/uninstall.
  • src/version.ts now resolves package.json relative to the built module, not process.cwd().
  • SubtleCaptions second line now scales font size consistently.
  • clip_history check now accepts and forwards format.
  • Format type is now used across the relevant model fields.
  • Content Studio actions are mutually guarded while one is in flight.
  • Downloading a new video clears stale preview state.
  • Layout logs non-OK /api/session-cache/clear responses.
  • Session cache clear now refuses while transcribe/create/batch jobs are running.
  • Removed duplicate CSS border variable.
  • Added stderr details to non-throwing proc debug logs.
  • Added warning logs for custom content generation failures and prompt cleanup failures.

Skipped:

  • yt-dlp upper bound/pin: no known-good ceiling is defined in the repo.
  • ClipDetail/ThumbnailStudio shared hook refactor: valid cleanup, but not a bug and would add abstraction.

Validation passed:

  • npm.cmd run build
  • python -m unittest tests.test_transcription_engine tests.test_transcript_cache_engine
  • python -m py_compile ...
  • git diff --check

@teethatkamsai teethatkamsai force-pushed the codex/uninstall-cleans-all branch from 9a61ca4 to 12a18eb Compare July 6, 2026 05:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
src/ui/client/EpisodeWorkspace.jsx (1)

615-626: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Preset video swap leaves stale preview state.

When loadPreset switches to a different video_path, this reset clears transcript/suggestions/results/energy but not previewSrc/activeClipIdx. A previously rendered-clip preview will keep showing against the new video — the same stale-preview issue the download flow now guards against (Lines 985-986).

🧹 Proposed fix
               setResults([]);
               setEnergyData({});
+              setPreviewSrc(null);
+              setActiveClipIdx(null);
               autoTranscribeRef.current = '';
               setPhase('idle');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/client/EpisodeWorkspace.jsx` around lines 615 - 626, The reset in the
`loadPreset`/`changedVideo` path clears transcript and result state, but it
leaves preview-related state from the previous video behind. Update
`EpisodeWorkspace` so the same reset also clears `previewSrc` and
`activeClipIdx` whenever `changedVideo` is true, matching the stale-preview
handling already used in the download flow and preventing a rendered clip from
persisting after a preset video swap.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/ui/client/EpisodeWorkspace.jsx`:
- Around line 615-626: The reset in the `loadPreset`/`changedVideo` path clears
transcript and result state, but it leaves preview-related state from the
previous video behind. Update `EpisodeWorkspace` so the same reset also clears
`previewSrc` and `activeClipIdx` whenever `changedVideo` is true, matching the
stale-preview handling already used in the download flow and preventing a
rendered clip from persisting after a preset video swap.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58a441cf-9de7-4c05-82cc-b6a44d758251

📥 Commits

Reviewing files that changed from the base of the PR and between 5e5bc27 and 9a61ca4.

📒 Files selected for processing (19)
  • .github/workflows/release.yml
  • backend/cli.py
  • backend/clip_studio.py
  • backend/main.py
  • backend/services/content_generator.py
  • backend/services/transcript_packer.py
  • backend/services/transcription.py
  • backend/utils/proc.py
  • install.ps1
  • remotion/src/components/SubtleCaptions.tsx
  • src/models/index.ts
  • src/server.ts
  • src/services/clips-history.ts
  • src/ui/client/ContentStudio.tsx
  • src/ui/client/EpisodeWorkspace.jsx
  • src/ui/client/Layout.tsx
  • src/ui/public/css/styles.css
  • src/ui/web-server.ts
  • src/version.ts
💤 Files with no reviewable changes (2)
  • backend/clip_studio.py
  • src/ui/public/css/styles.css
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/ui/client/Layout.tsx
  • .github/workflows/release.yml
  • backend/utils/proc.py
  • src/version.ts
  • src/services/clips-history.ts
  • remotion/src/components/SubtleCaptions.tsx
  • backend/services/transcript_packer.py
  • backend/services/transcription.py
  • install.ps1
  • src/server.ts
  • src/ui/client/ContentStudio.tsx

@nmbrthirteen nmbrthirteen left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Review notes on the AssemblyAI + version-alignment PR. Overall clean and well-tested; a few things worth fixing before merge, flagged inline (roughly high → low).

Comment thread src/ui/client/Layout.tsx
Comment thread remotion/src/components/SubtleCaptions.tsx
Comment thread src/services/transcript-cache.ts
Comment thread install.ps1 Outdated
Comment thread backend/cli.py Outdated
@teethatkamsai

Copy link
Copy Markdown
Contributor Author

Everything in those review notes is fixed on pushed commit 721ff11.

Checked:

  • Cache wipe: /api/session-cache/clear now only clears in-memory UI/session state, no transcript/packed/upload deletion.
  • Subtle captions: both caption lines use fontSize: style.fontSize * s.
  • Packed markdown engine: getPackedMarkdown callers now pass engine, including cached-result engine fallback.
  • Uninstall: plain -Uninstall preserves user data; -Purge removes $homeDir.
  • CLI key warning: --assemblyai-api-key help now warns to prefer ASSEMBLYAI_API_KEY because argv secrets can appear in process listings.

Re-ran:

  • npm.cmd test passed: 48/48.
  • npm.cmd run build passed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
src/handlers/transcribe.handler.ts (1)

102-119: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Cache transcripts under the resolved engine
cache.set(filePath, data, engine) uses the requested engine key, but data.engine can differ when whisper-py falls back to whispercpp. Because engineSuffix(undefined) and engineSuffix("whisper-py") both map to the same cache slot, that fallback transcript can be reused for later explicit whisper-py requests. Use the resolved engine for both cache write and packed lookup.

Suggested fix
-  await cache.set(filePath, data, engine);
-  const packed = await cache.getPackedMarkdown(filePath, data.engine ?? engine);
+  const resolvedEngine = data.engine ?? engine;
+  await cache.set(filePath, data, resolvedEngine);
+  const packed = await cache.getPackedMarkdown(filePath, resolvedEngine);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/handlers/transcribe.handler.ts` around lines 102 - 119, The transcript
cache is being written with the requested engine instead of the actual resolved
engine, which can cause fallback results to be reused incorrectly. Update the
caching flow in transcribe.handler.ts so the value used in cache.set and
cache.getPackedMarkdown comes from the resolved engine on data (for example
data.engine) rather than the original engine argument, and keep the same
resolved key consistently for both write and packed lookup.
🧹 Nitpick comments (2)
cli/main.go (1)

426-448: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider capturing stderr for clearer warning output.

removeFromWindowsUserPath runs a PowerShell script but doesn't wire cmd.Stderr, so any non-exit-2 failure surfaces only as err.Error() (e.g. exit status 1) in the warning at Line 377, without the underlying PowerShell error detail.

Diff to capture stderr for the warning message
+	var stderr bytes.Buffer
 	cmd := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", ps)
 	cmd.Env = append(os.Environ(), "PODCLI_REMOVE_PATH="+remove)
+	cmd.Stderr = &stderr
 	if err := cmd.Run(); err != nil {
 		if exit, ok := err.(*exec.ExitError); ok && exit.ExitCode() == 2 {
 			return false, nil
 		}
-		return false, err
+		return false, fmt.Errorf("%w: %s", err, stderr.String())
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/main.go` around lines 426 - 448, The removeFromWindowsUserPath PowerShell
execution hides the underlying failure details because cmd.Stderr is not
captured. Update removeFromWindowsUserPath to wire a stderr buffer (or
equivalent) into the exec.Command call, and return that captured PowerShell
error text when cmd.Run fails for any case other than exit code 2. Make sure the
warning path that reports the failure can surface the detailed stderr from the
PowerShell script instead of only the generic exec error.
backend/cli.py (1)

596-597: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Engine-matching tuple duplicated across three files.

The ("assemblyai", "assembly-ai", "aai") (and whispercpp) alias set is now hand-duplicated in cli.py (display label), transcription.py (transcribe_file), and transcript_packer.py (engine_cache_suffix). Any future engine alias added to one will silently desync the others (e.g., a display label that never says "AssemblyAI" while the cache still uses the AssemblyAI suffix). Consider exporting a single is_assemblyai_engine(name) / normalize_engine(name) helper from transcript_packer.py (or a new services/engines.py) for all three call sites to import.

Also applies to: 626-627

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/cli.py` around lines 596 - 597, The engine alias tuple is duplicated
across the CLI display label and the transcription/cache code, so the match
logic can drift between call sites. Extract a shared helper such as
is_assemblyai_engine(name) or normalize_engine(name) from transcript_packer.py
(or a new shared engines module) and use it in cli.py, transcribe_file in
transcription.py, and engine_cache_suffix in transcript_packer.py so all
AssemblyAI/Whisper alias handling stays consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/handlers/transcribe.handler.ts`:
- Around line 102-119: The transcript cache is being written with the requested
engine instead of the actual resolved engine, which can cause fallback results
to be reused incorrectly. Update the caching flow in transcribe.handler.ts so
the value used in cache.set and cache.getPackedMarkdown comes from the resolved
engine on data (for example data.engine) rather than the original engine
argument, and keep the same resolved key consistently for both write and packed
lookup.

---

Nitpick comments:
In `@backend/cli.py`:
- Around line 596-597: The engine alias tuple is duplicated across the CLI
display label and the transcription/cache code, so the match logic can drift
between call sites. Extract a shared helper such as is_assemblyai_engine(name)
or normalize_engine(name) from transcript_packer.py (or a new shared engines
module) and use it in cli.py, transcribe_file in transcription.py, and
engine_cache_suffix in transcript_packer.py so all AssemblyAI/Whisper alias
handling stays consistent.

In `@cli/main.go`:
- Around line 426-448: The removeFromWindowsUserPath PowerShell execution hides
the underlying failure details because cmd.Stderr is not captured. Update
removeFromWindowsUserPath to wire a stderr buffer (or equivalent) into the
exec.Command call, and return that captured PowerShell error text when cmd.Run
fails for any case other than exit code 2. Make sure the warning path that
reports the failure can surface the detailed stderr from the PowerShell script
instead of only the generic exec error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89b8c38c-873e-429f-ad0d-86f57cc4e6ab

📥 Commits

Reviewing files that changed from the base of the PR and between 9a61ca4 and 721ff11.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (31)
  • .github/workflows/release.yml
  • backend/cli.py
  • backend/clip_studio.py
  • backend/main.py
  • backend/services/content_generator.py
  • backend/services/env_settings.py
  • backend/services/transcript_packer.py
  • backend/services/transcription.py
  • backend/utils/proc.py
  • backend/version.py
  • cli/main.go
  • cli/main_test.go
  • install.ps1
  • package.json
  • remotion/src/components/SubtleCaptions.tsx
  • src/handlers/transcribe.handler.ts
  • src/models/index.ts
  • src/server.ts
  • src/services/clips-history.ts
  • src/services/transcript-cache.test.ts
  • src/services/transcript-cache.ts
  • src/ui/client/ConfigPage.tsx
  • src/ui/client/ContentStudio.tsx
  • src/ui/client/EpisodeWorkspace.jsx
  • src/ui/client/Layout.tsx
  • src/ui/client/StudioHome.tsx
  • src/ui/public/css/styles.css
  • src/ui/web-server.ts
  • src/version.ts
  • tests/test_transcript_cache_engine.py
  • tests/test_transcription_engine.py
✅ Files skipped from review due to trivial changes (4)
  • package.json
  • src/services/clips-history.ts
  • src/ui/public/css/styles.css
  • backend/utils/proc.py
🚧 Files skipped from review as they are similar to previous changes (13)
  • src/version.ts
  • backend/version.py
  • tests/test_transcription_engine.py
  • cli/main_test.go
  • backend/services/env_settings.py
  • src/ui/client/Layout.tsx
  • .github/workflows/release.yml
  • src/services/transcript-cache.ts
  • backend/clip_studio.py
  • backend/services/transcript_packer.py
  • tests/test_transcript_cache_engine.py
  • src/models/index.ts
  • src/ui/client/ContentStudio.tsx

@nmbrthirteen nmbrthirteen merged commit 3ea7755 into nmbrthirteen:main Jul 6, 2026
5 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.

2 participants