-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix(linux): add secretstorage to platform-critical packages (ACS-310) #1206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughThis PR refactors platform-specific dependency handling by adding Linux secretstorage validation with non-fatal warnings, consolidating Claude command construction to use pre-escaped config values, removing CI verdict re-evaluation logic in the review orchestrator, and reorganizing worktree UI selection with improved delete flows. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~65 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
Summary of ChangesHello @StillKnotKnown, 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 resolves a critical issue affecting Linux binary installations where the Highlights
Ignored Files
Using Gemini Code AssistThe 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
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 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
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this 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 addresses the missing secretstorage dependency on Linux, which is a valuable fix. The changes in dependency_validator.py and the corresponding tests are well-implemented.
However, this PR also includes a significant and unrelated refactoring of the terminal session persistence logic, changing it from asynchronous to synchronous writes. This is a major architectural change that can introduce performance bottlenecks by blocking the main UI thread, and it is not mentioned in the pull request description. Such a significant change should be in a separate pull request with a detailed explanation of the rationale and performance impact analysis. Mixing these two unrelated changes makes the PR difficult to review and increases risk. I strongly recommend splitting this PR into two: one for the secretstorage fix and another for the session persistence refactoring.
I am having trouble creating individual review comments. Click here to see my feedback.
apps/frontend/src/main/terminal-session-store.ts (78-95)
This change removes the asynchronous session saving logic and replaces it with synchronous writes (writeFileSync). This is a major architectural change that is not documented in the PR description. Synchronous file I/O on the main Electron process can block the UI thread, leading to a frozen or unresponsive application, especially if the session file is large or disk I/O is slow. This change should be reverted from this PR and moved to a separate one with a clear justification and performance analysis.
apps/frontend/scripts/download-python.cjs (720-726)
The logic for validating packages has been weakened. The previous implementation correctly checked for the existence of an __init__.py file within package directories, which is a more reliable way to verify a package. The new logic only checks for the directory's existence, which could lead to accepting corrupted or incomplete package directories as valid. This is a regression. The validation should check for either a valid package directory (with __init__.py) or a single module file.
const missingPackages = criticalPackages.filter(pkg => {
const pkgPath = path.join(sitePackagesDir, pkg);
const initFile = path.join(pkgPath, '__init__.py');
const moduleFile = path.join(sitePackagesDir, pkg + '.py');
// Package is considered missing if it's not a valid directory package AND not a single module file.
return !(fs.existsSync(pkgPath) && fs.existsSync(initFile)) && !fs.existsSync(moduleFile);
});
apps/frontend/src/main/python-env-manager.ts (143-150)
Similar to the issue in download-python.cjs, the package validation logic here has been weakened. The previous implementation correctly checked for an __init__.py file, which is a more robust way to verify a package directory. The new logic only checks for the directory's existence, which is a regression. The validation should check for either a valid package directory (with __init__.py) or a single module file.
const missingPackages = criticalPackages.filter((pkg) => {
const pkgPath = path.join(sitePackagesPath, pkg);
const initPath = path.join(pkgPath, '__init__.py');
const moduleFile = path.join(sitePackagesPath, `${pkg}.py`);
// Package is considered missing if it's not a valid directory package AND not a single module file.
return !existsSync(pkgPath) && !existsSync(initPath) && !existsSync(moduleFile);
});apps/backend/core/dependency_validator.py (64)
The function name _exit_with_secretstorage_warning is a bit misleading, as the function intentionally does not exit the program. A more descriptive name like _show_secretstorage_warning would better reflect its behavior, which is to print a warning to stderr and continue execution.
def _show_secretstorage_warning() -> None:
apps/frontend/scripts/download-python.cjs (714-719)
The logic to define criticalPackages is duplicated starting at line 825. This increases maintenance overhead and the risk of inconsistencies. This logic should be extracted into a reusable function within this script to ensure it's defined only once and called from both locations.
apps/frontend/src/main/python-env-manager.ts (131-140)
The logic for defining criticalPackages is duplicated from apps/frontend/scripts/download-python.cjs. The comment even notes that they should be kept in sync. This duplication increases maintenance overhead. This logic should be centralized in a shared utility or configuration file to avoid inconsistencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/frontend/src/main/terminal-session-store.ts (1)
177-207: Consider async I/O for session saves to prevent Electron main thread blocking.
saveSession()useswriteFileSync()for disk writes on the main thread. While current call frequency (every 30 seconds viapersistAllSessions()plus discrete lifecycle events) is moderate, synchronous I/O can still cause temporary UI freezes. Consider migrating towriteFile()or batching writes with a debounce mechanism similar tosession-persistence.ts.
🤖 Fix all issues with AI agents
In `@apps/backend/core/dependency_validator.py`:
- Around line 64-96: Rename the misleading function
_exit_with_secretstorage_warning to a name that reflects it does not exit
(suggest _warn_missing_secretstorage or _emit_secretstorage_warning), update all
references (e.g., calls from validate_platform_dependencies()), and update tests
in tests/test_dependency_validator.py accordingly; also add a flush of
sys.stderr after writing the warning so the message is immediately visible (call
sys.stderr.flush() right after the sys.stderr.write in the renamed function).
In `@apps/backend/spec/pipeline/agent_runner.py`:
- Around line 23-26: The module-level comment about lazy importing to avoid a
circular import should be moved (or duplicated with a short cross-reference) so
it's adjacent to the actual lazy import inside run_agent(); locate the lazy
import of create_client within the run_agent() function and place the
explanatory comment directly above that import (or leave a one-line pointer here
referencing run_agent() where the real import occurs) so future maintainers see
the rationale next to the code it documents.
In `@apps/frontend/scripts/download-python.cjs`:
- Around line 712-726: The package-existence check (building missingPackages
using platformCriticalPackages, criticalPackages, sitePackagesDir, pkgPath and
moduleFile) currently treats a directory as valid even if it lacks __init__.py;
change the condition so a package dir is only considered present if
path.join(pkgPath, '__init__.py') exists, while still allowing single-file
modules by checking pkg + '.py'; update both the pre-check (where
missingPackages is computed) and the equivalent post-install check to require
the __init__.py file for directory packages.
In `@apps/frontend/src/main/python-env-manager.ts`:
- Around line 143-149: The filter for missingPackages currently ignores the
presence of __init__.py (it only checks pkgPath and moduleFile); update the
predicate so a package is considered present only if its initPath exists or a
single-file module exists: replace the current return condition with one that
checks existsSync(initPath) or existsSync(moduleFile) (i.e., mark missing when
both are absent). Reference variables: missingPackages, criticalPackages,
pkgPath, initPath, moduleFile, and existsSync.
- Around line 131-140: The code currently indexes platformCriticalPackages using
process.platform in the criticalPackages initializer; replace that direct
platform access by using the platform helpers isWindows() and isLinux() to pick
the platform-specific arrays: use platformCriticalPackages['win32'] when
isWindows() is true and platformCriticalPackages['linux'] when isLinux() is true
(fall back to empty array otherwise) so that the constants
platformCriticalPackages and criticalPackages (referenced in this file) no
longer depend on process.platform directly; import isWindows and isLinux from
the platform module and use them in the criticalPackages construction.
In `@apps/frontend/src/main/terminal/session-handler.ts`:
- Around line 148-170: persistSession currently overwrites createdAt every time
it saves; modify persistSession(terminal: TerminalProcess) to check the existing
session in getTerminalSessionStore() (e.g., via store.getSession or equivalent
lookup by terminal.id) and if a session exists reuse its createdAt value when
building the TerminalSession object, otherwise set createdAt to new
Date().toISOString(); then call store.saveSession(session) as before so
createdAt is preserved for existing sessions.
In `@tests/test_dependency_validator.py`:
- Around line 192-216: The test test_linux_with_secretstorage_missing_warns
fails on Windows CI because it doesn't mock core.dependency_validator.is_windows
to False; update the test to patch core.dependency_validator.is_windows
returning False alongside the existing patch for is_linux so the Windows pywin32
path is skipped when calling validate_platform_dependencies(), ensuring the code
only exercises the Linux secretstorage branch and asserts
_exit_with_secretstorage_warning was called.
- Around line 218-233: The test
test_linux_with_secretstorage_installed_continues currently patches is_linux but
not is_windows, which can make validate_platform_dependencies() run the Windows
pywin32 check on Windows hosts; update the test to also patch
core.dependency_validator.is_windows to return False inside the same
with-context so validate_platform_dependencies() sees Linux=True and
Windows=False, ensuring the pywin32 branch is not executed and the test is
deterministic.
- Around line 235-253: Tests test_windows_skips_secretstorage_validation and
test_macos_skips_secretstorage_validation only patch is_linux but
validate_platform_dependencies() first queries is_windows(), so on Windows CI
the pywin32 check can run and break the test; update both tests to also patch
core.dependency_validator.is_windows to return False (mock is_windows) so
validate_platform_dependencies() skips the pywin32 branch, keeping the intent of
skipping secretstorage validation intact and preventing unintended pywin32
checks.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (11)
.github/workflows/test-azure-auth.ymlapps/backend/core/dependency_validator.pyapps/backend/spec/pipeline/agent_runner.pyapps/frontend/scripts/download-python.cjsapps/frontend/src/main/python-env-manager.tsapps/frontend/src/main/terminal-session-store.tsapps/frontend/src/main/terminal/claude-integration-handler.tsapps/frontend/src/main/terminal/session-handler.tsapps/frontend/src/main/terminal/terminal-lifecycle.tsapps/frontend/src/main/terminal/terminal-manager.tstests/test_dependency_validator.py
💤 Files with no reviewable changes (1)
- .github/workflows/test-azure-auth.yml
🧰 Additional context used
📓 Path-based instructions (8)
apps/frontend/src/**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/frontend/src/**/*.{tsx,ts}: All user-facing text in the frontend MUST use i18n translation keys fromreact-i18next, not hardcoded strings
Use translation key formatnamespace:section.key(e.g.,navigation:items.githubPRs) when referencing translations in code
For error messages with dynamic content, use i18n interpolation with syntax liket('errors:task.parseError', { error: errorMessage })
Files:
apps/frontend/src/main/python-env-manager.tsapps/frontend/src/main/terminal/terminal-lifecycle.tsapps/frontend/src/main/terminal/terminal-manager.tsapps/frontend/src/main/terminal/claude-integration-handler.tsapps/frontend/src/main/terminal/session-handler.tsapps/frontend/src/main/terminal-session-store.ts
**/*.{ts,tsx,js,py}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,py}: Do not checkprocess.platformdirectly in code - always import platform detection functions from the platform abstraction module
Never hardcode platform-specific paths likeC:\Program Files\,/opt/homebrew/bin/, or/usr/local/bin/directly in code
Files:
apps/frontend/src/main/python-env-manager.tsapps/frontend/src/main/terminal/terminal-lifecycle.tsapps/backend/spec/pipeline/agent_runner.pyapps/frontend/src/main/terminal/terminal-manager.tstests/test_dependency_validator.pyapps/frontend/src/main/terminal/claude-integration-handler.tsapps/frontend/src/main/terminal/session-handler.tsapps/backend/core/dependency_validator.pyapps/frontend/src/main/terminal-session-store.ts
apps/frontend/src/main/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use platform abstraction functions like
isWindows(),isMacOS(),isLinux(),getPathDelimiter(),getExecutableExtension(),findExecutable(),joinPaths()from the platform module instead of hardcoding paths or platform checks
Files:
apps/frontend/src/main/python-env-manager.tsapps/frontend/src/main/terminal/terminal-lifecycle.tsapps/frontend/src/main/terminal/terminal-manager.tsapps/frontend/src/main/terminal/claude-integration-handler.tsapps/frontend/src/main/terminal/session-handler.tsapps/frontend/src/main/terminal-session-store.ts
apps/frontend/**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
Frontend code must be built with Electron, React, and TypeScript
Files:
apps/frontend/src/main/python-env-manager.tsapps/frontend/src/main/terminal/terminal-lifecycle.tsapps/frontend/src/main/terminal/terminal-manager.tsapps/frontend/src/main/terminal/claude-integration-handler.tsapps/frontend/src/main/terminal/session-handler.tsapps/frontend/src/main/terminal-session-store.ts
apps/frontend/**/*.{ts,tsx}
⚙️ CodeRabbit configuration file
apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.
Files:
apps/frontend/src/main/python-env-manager.tsapps/frontend/src/main/terminal/terminal-lifecycle.tsapps/frontend/src/main/terminal/terminal-manager.tsapps/frontend/src/main/terminal/claude-integration-handler.tsapps/frontend/src/main/terminal/session-handler.tsapps/frontend/src/main/terminal-session-store.ts
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use the Claude Agent SDK (
claude-agent-sdkpackage) for all AI interactions - NEVER use the Anthropic API directly
Files:
apps/backend/spec/pipeline/agent_runner.pytests/test_dependency_validator.pyapps/backend/core/dependency_validator.py
apps/backend/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
apps/backend/**/*.py: Always import and usecreate_client()fromcore.clientinstead of instantiatinganthropic.Anthropic()directly for LLM client creation
Use the platform abstraction module for path handling and executable detection in Python backend code
All Python backend code must be Python 3.12+ compatible
Use Graphiti as the primary memory system for cross-session context, configured inapps/backend/graphiti_config.pywith multi-provider support (OpenAI, Anthropic, Azure, Ollama, Google AI)
Files:
apps/backend/spec/pipeline/agent_runner.pyapps/backend/core/dependency_validator.py
⚙️ CodeRabbit configuration file
apps/backend/**/*.py: Focus on Python best practices, type hints, and async patterns.
Check for proper error handling and security considerations.
Verify compatibility with Python 3.12+.
Files:
apps/backend/spec/pipeline/agent_runner.pyapps/backend/core/dependency_validator.py
tests/**
⚙️ CodeRabbit configuration file
tests/**: Ensure tests are comprehensive and follow pytest conventions.
Check for proper mocking and test isolation.
Files:
tests/test_dependency_validator.py
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.702Z
Learning: When fixing platform-specific bugs, ensure the fix doesn't break other platforms and rely on multi-platform CI to validate (Windows, macOS, Linux)
📚 Learning: 2026-01-16T09:10:31.701Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.701Z
Learning: Applies to **/*.{ts,tsx,js,py} : Do not check `process.platform` directly in code - always import platform detection functions from the platform abstraction module
Applied to files:
apps/frontend/src/main/python-env-manager.ts
📚 Learning: 2026-01-16T09:10:31.701Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.701Z
Learning: Applies to apps/frontend/src/main/**/*.{ts,tsx} : Use platform abstraction functions like `isWindows()`, `isMacOS()`, `isLinux()`, `getPathDelimiter()`, `getExecutableExtension()`, `findExecutable()`, `joinPaths()` from the platform module instead of hardcoding paths or platform checks
Applied to files:
apps/frontend/src/main/python-env-manager.tsapps/frontend/src/main/terminal-session-store.ts
📚 Learning: 2026-01-16T09:10:31.701Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.701Z
Learning: Applies to **/*.{ts,tsx,js,py} : Never hardcode platform-specific paths like `C:\Program Files\`, `/opt/homebrew/bin/`, or `/usr/local/bin/` directly in code
Applied to files:
apps/frontend/src/main/python-env-manager.tsapps/frontend/src/main/terminal-session-store.ts
📚 Learning: 2026-01-16T09:10:31.701Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.701Z
Learning: Applies to **/*.test.{ts,tsx,js,py} : Write tests for platform-specific code that mock `process.platform` or platform detection functions to test all three platforms (Windows, macOS, Linux)
Applied to files:
apps/frontend/src/main/python-env-manager.ts
📚 Learning: 2026-01-16T09:10:31.702Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.702Z
Learning: When fixing platform-specific bugs, ensure the fix doesn't break other platforms and rely on multi-platform CI to validate (Windows, macOS, Linux)
Applied to files:
apps/frontend/src/main/python-env-manager.ts
📚 Learning: 2026-01-16T09:10:31.701Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.701Z
Learning: Applies to apps/backend/**/*.py : Always import and use `create_client()` from `core.client` instead of instantiating `anthropic.Anthropic()` directly for LLM client creation
Applied to files:
apps/backend/spec/pipeline/agent_runner.py
📚 Learning: 2026-01-16T09:10:31.701Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.701Z
Learning: Applies to apps/backend/core/client.py : Agent-specific tool permissions are configured through `create_client()` based on agent type (planner, coder, qa_reviewer, qa_fixer)
Applied to files:
apps/backend/spec/pipeline/agent_runner.py
📚 Learning: 2026-01-16T09:10:31.701Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.701Z
Learning: Applies to apps/backend/**/*.py : Use the platform abstraction module for path handling and executable detection in Python backend code
Applied to files:
tests/test_dependency_validator.py
📚 Learning: 2026-01-11T10:48:12.867Z
Learnt from: g1331
Repo: AndyMik90/Auto-Claude PR: 912
File: tests/test_issue_884_plan_schema.py:287-296
Timestamp: 2026-01-11T10:48:12.867Z
Learning: In test files under tests/, prefer keeping small helper classes (e.g., DummyClient) local to individual test functions when duplication is minimal, tests are integration-style and benefit from self-containment, and tests/conftest.py is already large. Extract helpers to conftest only when duplication across tests is significant or when shared setup is truly reusable across many tests.
Applied to files:
tests/test_dependency_validator.py
📚 Learning: 2026-01-16T09:10:31.701Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-16T09:10:31.701Z
Learning: Applies to apps/frontend/**/*.{ts,tsx,js} : Frontend code must be built with Electron, React, and TypeScript
Applied to files:
apps/frontend/src/main/terminal-session-store.ts
🧬 Code graph analysis (4)
apps/frontend/src/main/python-env-manager.ts (1)
apps/frontend/scripts/download-python.cjs (1)
path(20-20)
apps/backend/spec/pipeline/agent_runner.py (1)
apps/backend/core/client.py (1)
create_client(660-1052)
apps/frontend/src/main/terminal/session-handler.ts (2)
apps/frontend/src/main/terminal/types.ts (1)
TerminalProcess(8-24)apps/frontend/src/main/terminal-session-store.ts (2)
getTerminalSessionStore(447-452)TerminalSession(9-21)
apps/backend/core/dependency_validator.py (1)
apps/backend/core/platform/__init__.py (2)
is_linux(76-78)is_windows(66-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Seer Code Review
- GitHub Check: test-frontend (windows-latest)
- GitHub Check: CodeQL (javascript-typescript)
- GitHub Check: CodeQL (python)
🔇 Additional comments (13)
apps/backend/spec/pipeline/agent_runner.py (1)
122-124: LGTM! Lazy import correctly breaks circular dependency.The lazy import of
create_clientinsiderun_agent()is a valid pattern for breaking the circular import chain. Python caches module imports, so subsequent calls torun_agent()will reuse the cached module without re-importing. The import source (core.client) complies with coding guidelines.tests/test_dependency_validator.py (1)
261-313: LGTM! Comprehensive tests for_exit_with_secretstorage_warning.The tests thoroughly verify:
- Warning message contains helpful installation instructions
- Fallback behavior (.env) is documented in the warning
- Virtual environment path is included
- Warning does NOT call
sys.exit()(non-blocking behavior)This aligns well with the PR objective of emitting a warning rather than blocking error for missing secretstorage.
apps/frontend/src/main/terminal/terminal-manager.ts (2)
42-44: Verify synchronous persistence won't block the main Electron process.The periodic save timer now calls
persistAllSessionssynchronously every 30 seconds. If this involves significant I/O (writing to disk), it could cause brief UI freezes in the Electron main process. Ensure the underlying implementation insession-handler.tsuses efficient synchronous operations (e.g.,fs.writeFileSyncwith small payloads) or consider keeping async persistence for the timer path to avoid blocking the event loop.
371-373: LGTM! Synchronous persistence is appropriate here.Using
persistSessionsynchronously when worktree config changes is reasonable since this is triggered by explicit user action and immediate persistence ensures data consistency.apps/frontend/src/main/terminal/terminal-lifecycle.ts (4)
100-102: LGTM! Synchronous persistence at creation is acceptable.Persisting session data synchronously immediately after terminal creation ensures the session is saved before any subsequent operations. The payload is typically small (single session object), so blocking impact should be minimal.
185-187: LGTM! Correct re-persistence after restoring session metadata.Persisting after restoring title and worktreeConfig ensures the updated state is saved, which is necessary since
createTerminalpersisted before these values were set.
219-222: LGTM! Persistence of Claude mode state is correctly placed.Persisting the Claude mode and pending resume state ensures these flags survive app restarts, enabling proper session restoration behavior.
279-279: LGTM! Synchronous persistence before shutdown is appropriate.Using synchronous
persistAllSessionsbefore destroying all terminals during app shutdown ensures all session state is written to disk before the process terminates. This prevents data loss.apps/backend/core/dependency_validator.py (2)
11-11: LGTM! Correctly uses platform abstraction module.Importing
is_linuxandis_windowsfromcore.platformcomplies with the coding guidelines that prohibit directsys.platformchecks. This ensures consistent platform detection across the codebase.
29-36: LGTM! Non-blocking validation for optional dependency.The secretstorage check correctly emits a warning instead of exiting, allowing the app to fall back to
.envstorage. This mirrors the pattern mentioned in the PR objectives for Windows pywin32 (ACS-306) but appropriately differs in severity since secretstorage is optional.apps/frontend/src/main/terminal/session-handler.ts (1)
173-182: LGTM — centralized persistence path.Delegating to
persistSession()keeps the guardrails consistent and simplifies the multi-terminal flow.apps/frontend/src/main/terminal/claude-integration-handler.ts (1)
905-934: LGTM — sync persistence aligned with new API.The resume flow now uses the unified
persistSessionentry point.apps/frontend/src/main/terminal-session-store.ts (1)
1-3: LGTM — import aligns with sync persistence.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| /** | ||
| * Create a TerminalSession object from a TerminalProcess. | ||
| * Shared helper used by both persistSession and persistSessionAsync. | ||
| * Persist a terminal session to disk | ||
| */ | ||
| function createSessionObject(terminal: TerminalProcess): TerminalSession { | ||
| return { | ||
| export function persistSession(terminal: TerminalProcess): void { | ||
| if (!terminal.projectPath) { | ||
| return; | ||
| } | ||
|
|
||
| const store = getTerminalSessionStore(); | ||
| const session: TerminalSession = { | ||
| id: terminal.id, | ||
| title: terminal.title, | ||
| cwd: terminal.cwd, | ||
| projectPath: terminal.projectPath!, | ||
| projectPath: terminal.projectPath, | ||
| isClaudeMode: terminal.isClaudeMode, | ||
| claudeSessionId: terminal.claudeSessionId, | ||
| outputBuffer: terminal.outputBuffer, | ||
| createdAt: new Date().toISOString(), | ||
| lastActiveAt: new Date().toISOString(), | ||
| worktreeConfig: terminal.worktreeConfig, | ||
| }; | ||
| store.saveSession(session); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preserve createdAt when re-saving existing sessions.
persistSession() sets createdAt to new Date() on every save, which overwrites the original creation timestamp. If createdAt is meant to represent the initial session creation, this silently changes semantics and can skew ordering/history.
Consider reusing the stored createdAt when the session already exists:
🛠️ Proposed fix
export function persistSession(terminal: TerminalProcess): void {
if (!terminal.projectPath) {
return;
}
const store = getTerminalSessionStore();
+ const now = new Date().toISOString();
+ const existing = store.getSession(terminal.projectPath, terminal.id);
const session: TerminalSession = {
id: terminal.id,
title: terminal.title,
cwd: terminal.cwd,
projectPath: terminal.projectPath,
isClaudeMode: terminal.isClaudeMode,
claudeSessionId: terminal.claudeSessionId,
outputBuffer: terminal.outputBuffer,
- createdAt: new Date().toISOString(),
- lastActiveAt: new Date().toISOString(),
+ createdAt: existing?.createdAt ?? now,
+ lastActiveAt: now,
worktreeConfig: terminal.worktreeConfig,
};
store.saveSession(session);
}🤖 Prompt for AI Agents
In `@apps/frontend/src/main/terminal/session-handler.ts` around lines 148 - 170,
persistSession currently overwrites createdAt every time it saves; modify
persistSession(terminal: TerminalProcess) to check the existing session in
getTerminalSessionStore() (e.g., via store.getSession or equivalent lookup by
terminal.id) and if a session exists reuse its createdAt value when building the
TerminalSession object, otherwise set createdAt to new Date().toISOString();
then call store.saveSession(session) as before so createdAt is preserved for
existing sessions.
| // For single-file modules (like pywintypes.py), check for the file directly | ||
| const moduleFile = path.join(sitePackagesDir, pkg + '.py'); | ||
| // Package is valid if directory+__init__.py exists OR single-file module exists | ||
| return !fs.existsSync(pkgPath) && !fs.existsSync(moduleFile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: The new validation logic no longer checks for __init__.py in directory-based packages, which could allow a corrupted package to be considered valid.
Severity: CRITICAL
Suggested Fix
The validation logic should differentiate between directory-based packages and single-file modules. For directory packages, it should verify the existence of both the directory and its __init__.py file. For single-file modules, it should check for the .py file. A possible implementation is !fs.existsSync(moduleFile) && !(fs.existsSync(pkgPath) && fs.existsSync(initFile)), which ensures one of the two valid package structures exists.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: apps/frontend/scripts/download-python.cjs#L725
Potential issue: The updated package validation logic incorrectly approves
directory-based packages without verifying the presence of an `__init__.py` file. The
new condition `!fs.existsSync(pkgPath) && !fs.existsSync(moduleFile)` checks if both a
package directory and a single-file module are missing. For a directory-based package
like `secretstorage`, if the directory exists but its `__init__.py` is missing due to a
corrupted installation, the check will incorrectly pass. This will cause the application
to fail at runtime with an `ImportError` when it attempts to import the corrupted
package, defeating the purpose of the validation.
Did we get this right? 👍 / 👎 to inform future reviews.
00a44a2 to
52aecc7
Compare
| # Get the default collection (typically "login" keyring) | ||
| # secretstorage handles DBus communication internally | ||
| try: | ||
| collection = secretstorage.get_default_collection(None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: The call to secretstorage.get_default_collection(None) is incorrect. It requires a valid DBusConnection object, not None, which will cause a runtime error on Linux.
Severity: CRITICAL
Suggested Fix
Initialize a DBus connection before getting the collection. First, call connection = secretstorage.dbus_init(). Then, pass the created connection object to the collection function like so: collection = secretstorage.get_default_collection(connection).
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: apps/backend/core/auth.py#L167
Potential issue: The function `get_default_collection` from the `secretstorage` library
is called with `None` as its first argument. According to the library's documentation,
this argument is mandatory and must be a `DBusConnection` object, which is obtained by
calling `secretstorage.dbus_init()`. This incorrect usage will lead to a runtime error,
such as a `TypeError` or `AttributeError`, when the code is executed on a Linux system.
This will cause the retrieval of OAuth tokens from the Linux keyring to fail. The unit
tests do not catch this issue because they mock the
`secretstorage.get_default_collection` call, bypassing the actual library's API
validation.
Did we get this right? 👍 / 👎 to inform future reviews.
| f"[DEBUG {context_name}] Tool result: {tool_id} [{status}]" | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: The function process_sdk_stream will crash with a NameError because it uses the variables last_progress_log, PROGRESS_LOG_INTERVAL, and model_info without initializing them.
Severity: CRITICAL
Suggested Fix
Initialize last_progress_log = 0 and define a constant PROGRESS_LOG_INTERVAL at the start of the function. For model_info, use the provided model parameter and the _short_model_name helper, for example: model_info = f" ({_short_model_name(model)})" if model else "". Also, remove the duplicate safe_print statement.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: apps/backend/runners/github/services/sdk_utils.py#L182-L183
Potential issue: The function `process_sdk_stream` references three variables that are
never initialized: `last_progress_log`, `PROGRESS_LOG_INTERVAL`, and `model_info`. The
first use of `last_progress_log` in a conditional check on line 182 will immediately
raise a `NameError` at runtime, as it has no starting value. Similarly, `model_info` is
used in a log message on line 339 without being defined. Since this function is called
during PR review operations, any such operation will crash, preventing the feature from
working.
Did we get this right? 👍 / 👎 to inform future reviews.
52aecc7 to
f6f66a1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/frontend/src/__tests__/integration/terminal-copy-paste.test.ts (1)
259-432: Add macOS platform test for clipboard paste integration.The clipboard read tests cover Windows (line 279) and Linux (line 358), but macOS is not tested. Based on learnings, platform-specific code should test all three platforms.
♻️ Suggested test case for macOS
Add a test for macOS platform alongside the existing Windows and Linux tests:
it('should integrate clipboard.readText() with xterm.paste() on macOS', async () => { const { useXterm } = await import('../../renderer/components/terminal/useXterm'); // Mock macOS platform Object.defineProperty(navigator, 'platform', { value: 'MacIntel', writable: true }); let keyEventHandler: ((event: KeyboardEvent) => boolean) | null = null; const mockPaste = vi.fn(); // ... rest of test setup similar to Windows test ... // Test with metaKey instead of ctrlKey for macOS await act(async () => { const event = new KeyboardEvent('keydown', { key: 'v', metaKey: true // Cmd+V on macOS }); if (keyEventHandler) { keyEventHandler(event); await new Promise(resolve => setTimeout(resolve, 0)); } }); expect(mockClipboard.readText).toHaveBeenCalled(); expect(mockPaste).toHaveBeenCalledWith('pasted text'); });
f6f66a1 to
e8cf42b
Compare
* ci: add Azure auth test workflow * fix(worktree): handle "already up to date" case correctly (ACS-226) (AndyMik90#961) * fix(worktree): handle "already up to date" case correctly (ACS-226) When git merge returns non-zero for "Already up to date", the merge code incorrectly treated this as a conflict and aborted. Now checks git output to distinguish between: - "Already up to date" - treat as success (nothing to merge) - Actual conflicts - abort as before - Other errors - show actual error message Also added comprehensive tests for edge cases: - Already up to date with no_commit=True - Already up to date with delete_after=True - Actual merge conflict detection - Merge conflict with no_commit=True * test: strengthen merge conflict abort verification Improve assertions in conflict detection tests to explicitly verify: - MERGE_HEAD does not exist after merge abort - git status returns clean (no staged/unstaged changes) This is more robust than just checking for absence of "CONFLICT" string, as git status --porcelain uses status codes, not literal words. * test: add git command success assertions and branch deletion verification - Add explicit returncode assertions for all subprocess.run git add/commit calls - Add branch deletion verification in test_merge_worktree_already_up_to_date_with_delete_after - Ensures tests fail early if git commands fail rather than continuing silently --------- Co-authored-by: StillKnotKnown <[email protected]> * fix(terminal): add collision detection for terminal drag and drop reordering (AndyMik90#985) * fix(terminal): add collision detection for terminal drag and drop reordering Add closestCenter collision detection to DndContext to fix terminal drag and drop swapping not detecting valid drop targets. The default rectIntersection algorithm required too much overlap for grid layouts. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(terminal): handle file drops when closestCenter returns sortable ID Address PR review feedback: - Fix file drop handling to work when closestCenter collision detection returns the sortable ID instead of the droppable ID - Add terminals to useCallback dependency array to prevent stale state Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(ACS-181): enable auto-switch on 401 auth errors & OAuth-only profiles (AndyMik90#900) * fix(ACS-181): enable auto-switch for OAuth-only profiles Add OAuth token check at the start of isProfileAuthenticated() so that profiles with only an oauthToken (no configDir) are recognized as authenticated. This allows the profile scorer to consider OAuth-only profiles as valid alternatives for proactive auto-switching. Previously, isProfileAuthenticated() immediately returned false if configDir was missing, causing OAuth-only profiles to receive a -500 penalty in the scorer and never be selected for auto-switch. Fixes: ACS-181 Co-Authored-By: Claude Opus 4.5 <[email protected]> Signed-off-by: Black Circle Sentinel <[email protected]> * fix(ACS-181): detect 'out of extra usage' rate limit messages The previous patterns only matched "Limit reached · resets ..." but Claude Code also shows "You're out of extra usage · resets ..." which wasn't being detected. This prevented auto-switch from triggering. Added new patterns to both output-parser.ts (terminal) and rate-limit-detector.ts (agent processes) to detect: - "out of extra usage · resets ..." - "You're out of extra usage · resets ..." Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(ACS-181): add real-time rate limit detection and debug logging - Add real-time rate limit detection in agent-process.ts processLog() so rate limits are detected immediately as output appears, not just when the process exits - Add clear warning message when auto-switch is disabled in settings - Add debug logging to profile-scorer.ts to trace profile evaluation - Add debug logging to rate-limit-detector.ts to trace pattern matching This enables immediate detection and auto-switch when rate limits occur during task execution. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(frontend): enable auto-switch on 401 auth errors - Propagate 401/403 errors from fetchUsageViaAPI to checkUsageAndSwap in UsageMonitor to trigger proactive profile swapping. - Fix usage monitor race condition by ensuring it waits for ClaudeProfileManager initialization. - Fix isProfileAuthenticated to correctly validate OAuth-only profiles. * fix(ACS-181): address PR review feedback - Revert unrelated files (rate-limit-detector, output-parser, agent-process) to upstream state - Gate profile-scorer logging behind DEBUG flag - Fix usage-monitor type safety and correct catch block syntax - Fix misleading indentation in index.ts app updater block * fix(frontend): enforce eslint compliance for logs in profile-scorer - Replace all console.log with console.warn (per linter rules) - Strictly gate all debug logs behind isDebug check to prevent production noise * fix(ACS-181): add swap loop protection for auth failures - Add authFailedProfiles Map to track profiles with recent auth failures - Implement 5-minute cooldown before retrying failed profiles - Exclude failed profiles from swap candidates to prevent infinite loops - Gate TRACE logs behind DEBUG flag to reduce production noise - Change console.log to console.warn for ESLint compliance --------- Signed-off-by: Black Circle Sentinel <[email protected]> Co-authored-by: Claude Opus 4.5 <[email protected]> * feat(frontend): add Claude Code version rollback feature (AndyMik90#983) * feat(frontend): add Claude Code version rollback feature Add ability for users to switch to any of the last 20 Claude Code CLI versions directly from the Claude Code popup in the sidebar. Changes: - Add IPC channels for fetching available versions and installing specific version - Add backend handlers to fetch versions from npm registry (with 1-hour cache) - Add version selector dropdown in ClaudeCodeStatusBadge component - Add warning dialog before switching versions (warns about closing sessions) - Add i18n support for English and French translations Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: address PR review feedback for Claude Code version rollback - Add validation after semver filtering to handle empty version list - Add error state and UI feedback for installation/version switch failures - Extract magic number 5000ms to VERSION_RECHECK_DELAY_MS constant - Bind Select value prop to selectedVersion state - Normalize version comparison to handle 'v' prefix consistently - Use normalized version comparison in SelectItem disabled check Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(security): inherit security profiles in worktrees and validate shell -c commands (AndyMik90#971) * fix(security): inherit security profiles in worktrees and validate shell -c commands - Add inherited_from field to SecurityProfile to mark profiles copied from parent projects - Skip hash-based re-analysis for inherited profiles (fixes worktrees losing npm/npx etc.) - Add shell_validators.py to validate commands inside bash/sh/zsh -c strings - Register shell validators to close security bypass via bash -c "arbitrary_command" - Add 13 new tests for inherited profiles and shell -c validation Fixes worktree security config not being inherited, which caused agents to be blocked from running npm/npx commands in isolated workspaces. * docs: update README download links to v2.7.3 (AndyMik90#976) - Update all stable download links from 2.7.2 to 2.7.3 - Add Flatpak download link (new in 2.7.3) * fix(security): close shell -c bypass vectors and validate inherited profiles - Fix combined shell flags bypass (-xc, -ec, -ic) in _extract_c_argument() Shell allows combining flags like `bash -xc 'cmd'` which bypassed -c detection - Add recursive validation for nested shell invocations Prevents bypass via `bash -c "bash -c 'evil_cmd'"` - Validate inherited_from path in should_reanalyze() with defense-in-depth - Must exist and be a directory - Must be an ancestor of current project - Must contain valid security profile - Add comprehensive test coverage for all security fixes Co-Authored-By: Claude Opus 4.5 <[email protected]> * style: fix import ordering in test_security.py Co-Authored-By: Claude Opus 4.5 <[email protected]> * style: format shell_validators.py Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * feat(frontend): add searchable branch combobox to worktree creation dialog (AndyMik90#979) * feat(frontend): add searchable branch combobox to worktree creation dialog - Replace limited Select dropdown with searchable Combobox for branch selection - Add new Combobox UI component with search filtering and scroll support - Remove 15-branch limit - now shows all branches with search - Improve worktree name validation to allow dots and underscores - Better sanitization: spaces become hyphens, preserve valid characters - Add i18n keys for branch search UI in English and French Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(frontend): address PR review feedback for worktree dialog - Extract sanitizeWorktreeName utility function to avoid duplication - Replace invalid chars with hyphens instead of removing them (feat/new → feat-new) - Trim trailing hyphens and dots from sanitized names - Add validation to forbid '..' in names (invalid for Git branch names) - Refactor branchOptions to use map/spread instead of forEach/push - Add ARIA accessibility: listboxId, aria-controls, role="listbox" Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(frontend): align worktree name validation with backend regex - Fix frontend validation to match backend WORKTREE_NAME_REGEX (no dots, must end with alphanumeric) - Update sanitizeWorktreeName to exclude dots from allowed characters - Update i18n messages (en/fr) to remove mention of dots - Add displayName to Combobox component for React DevTools - Export Combobox from UI component index.ts - Add aria-label to Combobox listbox for accessibility Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(frontend): address PR review accessibility and cleanup issues - Add forwardRef pattern to Combobox for consistency with other UI components - Add keyboard navigation (ArrowUp/Down, Enter, Escape, Home, End) - Add aria-activedescendant for screen reader focus tracking - Add unique option IDs for ARIA compliance - Add cleanup for async branch fetching to prevent state updates on unmounted component Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(frontend): sync worktree config to renderer on terminal restoration (AndyMik90#982) * fix(frontend): sync worktree config to renderer on terminal restoration When terminals are restored after app restart, the worktree config was not being synced to the renderer, causing the worktree label to not appear. This adds a new IPC channel to send worktree config during restoration and a listener in useTerminalEvents to update the terminal store. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(frontend): always sync worktreeConfig to handle deleted worktrees Addresses PR review feedback: send worktreeConfig IPC message unconditionally so the renderer can clear stale worktree labels when a worktree is deleted while the app is closed. Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(merge): include files with content changes even when semantic analysis is empty (AndyMik90#986) * fix(merge): include files with content changes even when semantic analysis is empty The merge system was discarding files that had real code changes but no detected semantic changes. This happened because: 1. The semantic analyzer only detects imports and function additions/removals 2. Files with only function body modifications returned semantic_changes=[] 3. The filter used Python truthiness (empty list = False), excluding these files 4. This caused merges to fail with "0 files to merge" despite real changes The fix uses content hash comparison as a fallback check. If the file content actually changed (hash_before != hash_after), include it for merge regardless of whether the semantic analyzer could parse the specific change types. This fixes merging for: - Files with function body modifications (most common case) - Unsupported file types (Rust, Go, etc.) where semantic analysis returns empty - Any file where the analyzer fails to detect the specific change pattern Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor(merge): add TaskSnapshot.has_modifications property and handle DIRECT_COPY Address PR review feedback: 1. DRY improvement: Add `has_modifications` property to TaskSnapshot - Centralizes the modification detection logic - Checks semantic_changes first, falls back to content hash comparison - Handles both complete tasks and in-progress tasks safely 2. Fix for files with empty semantic_changes (Cursor issue #2): - Add DIRECT_COPY MergeDecision for files that were modified but couldn't be semantically analyzed (body changes, unsupported languages) - MergePipeline returns DIRECT_COPY when has_modifications=True but semantic_changes=[] (single task case) - Orchestrator handles DIRECT_COPY by reading file directly from worktree - This prevents silent data loss where apply_single_task_changes would return baseline content unchanged 3. Update _update_stats to count DIRECT_COPY as auto-merged The combination ensures: - Files ARE detected for merge (has_modifications check) - Files ARE properly merged (DIRECT_COPY reads from worktree) - No silent data loss (worktree content used instead of baseline) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(merge): handle DIRECT_COPY in merge_tasks() and log missing files - Add DIRECT_COPY handling to merge_tasks() for multi-task merges (was only handled in merge_task() for single-task merges) - Add warning logging when worktree file doesn't exist during DIRECT_COPY in both merge_task() and merge_tasks() Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(merge): remove unnecessary f-string prefixes Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(merge): properly fail DIRECT_COPY when worktree file missing - Extract _read_worktree_file_for_direct_copy() helper to DRY up logic - Set decision to FAILED when worktree file not found (was silent success) - Add warning when worktree_path is None in merge_tasks - Use `is not None` check for merged_content to allow empty files - Fix has_modifications for new files with empty hash_before - Add debug_error() to merge_tasks exception handling for consistency Co-Authored-By: Claude Opus 4.5 <[email protected]> * style(merge): fix ruff formatting for long line Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(terminal): detect Claude exit and reset label when user closes Claude (AndyMik90#990) * fix(terminal): detect Claude exit and reset label when user closes Claude Previously, the "Claude" label on terminals would persist even after the user closed Claude (via /exit, Ctrl+D, etc.) because the system only reset isClaudeMode when the entire terminal process exited. This change adds robust Claude exit detection by: - Adding shell prompt patterns to detect when Claude exits and returns to shell (output-parser.ts) - Adding new IPC channel TERMINAL_CLAUDE_EXIT for exit notifications - Adding handleClaudeExit() to reset terminal state in main process - Adding onClaudeExit callback in terminal event handler - Adding onTerminalClaudeExit listener in preload API - Handling exit event in renderer to update terminal store Now when a user closes Claude within a terminal, the label is removed immediately while the terminal continues running. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(terminal): add line-start anchors to exit detection regex patterns Address PR review findings: - Add ^ anchors to CLAUDE_EXIT_PATTERNS to prevent false positive exit detection when Claude outputs paths, array access, or Unicode arrows - Add comprehensive unit tests for detectClaudeExit and related functions - Remove duplicate debugLog call in handleClaudeExit (keep console.warn) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(terminal): prevent false exit detection for emails and race condition - Update user@host regex to require path indicator after colon, preventing emails like [email protected]: from triggering exit detection - Add test cases for emails at line start to ensure they don't match - Add guard in onTerminalClaudeExit to prevent setting status to 'running' if terminal has already exited (fixes potential race condition) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(app-update): persist downloaded update state for Install button visibility (AndyMik90#992) * fix(app-update): persist downloaded update state for Install button visibility When updates auto-download in background, users miss the update-downloaded event if not on Settings page. This causes "Install and Restart" button to never appear. Changes: - Add downloadedUpdateInfo state in app-updater.ts to persist downloaded info - Add APP_UPDATE_GET_DOWNLOADED IPC handler to query downloaded state - Add getDownloadedAppUpdate API method in preload - Update AdvancedSettings to check for already-downloaded updates on mount Now when user opens Settings after background download, the component queries persisted state and shows "Install and Restart" correctly. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(app-update): resolve race condition and type safety issues - Fix race condition where checkForAppUpdates() could overwrite downloaded update info with null, causing 'Unknown' version display - Add proper type guard for releaseNotes (can be string | array | null) instead of unsafe type assertion Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(app-update): clear downloaded update state on channel change and add useEffect cleanup - Clear downloadedUpdateInfo when update channel changes to prevent showing Install button for updates from a different channel (e.g., beta update showing after switching to stable channel) - Add isCancelled flag to useEffect async operations in AdvancedSettings to prevent React state updates on unmounted components Addresses CodeRabbit review findings. Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(backend): add Sentry integration and fix broken pipe errors (AndyMik90#991) * fix(backend): add Sentry integration and fix broken pipe errors - Add sentry-sdk to Python backend for error tracking - Create safe_print() utility to handle BrokenPipeError gracefully - Initialize Sentry in CLI, GitHub runner, and spec runner entry points - Use same SENTRY_DSN environment variable as Electron frontend - Apply privacy path masking (usernames removed from stack traces) Fixes "Review Failed: [Errno 32] Broken pipe" error in PR review Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(backend): address PR review findings for Sentry integration - Fix ruff linting errors (unused imports, import sorting) - Add path masking to set_context() and set_tag() for privacy - Add defensive path masking to capture_exception() kwargs - Add debug logging for bare except clauses in sentry.py - Add top-level error handler in cli/main.py with Sentry capture - Add error handling with Sentry capture in spec_runner.py - Move safe_print to core/io_utils.py for broader reuse - Migrate GitLab runner files to use safe_print() - Add fallback import pattern in sdk_utils.py Co-Authored-By: Claude Opus 4.5 <[email protected]> * style: apply ruff formatting Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(backend): address CodeRabbit review findings for Sentry and io_utils - Add path masking to capture_message() kwargs for privacy consistency - Add recursion depth limit (50) to _mask_object_paths() to prevent stack overflow - Add WSL path masking support (/mnt/[a-z]/Users/...) - Add consistent ImportError debug logging across Sentry wrapper functions - Add ValueError handling in safe_print() for closed stdout scenarios - Improve reset_pipe_state() documentation with usage warnings Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix: improve Claude CLI detection and add installation selector (AndyMik90#1004) * fix: improve Claude CLI detection and add installation selector This PR addresses the "Claude Code not found" error when starting tasks by improving CLI path detection across all platforms. Backend changes: - Add cross-platform `find_claude_cli()` function in `client.py` that checks: - CLAUDE_CLI_PATH environment variable for user override - System PATH via shutil.which() - Homebrew paths on macOS - NVM paths for Node.js version manager installations - Platform-specific standard locations (Windows: AppData, Program Files; Unix: .local/bin) - Pass detected `cli_path` to ClaudeAgentOptions in both `create_client()` and `create_simple_client()` - Improve Windows .cmd/.bat file execution using proper cmd.exe flags (/d, /s, /c) and correct quoting for paths with spaces Frontend changes: - Add IPC handlers for scanning all Claude CLI installations and switching active path - Update ClaudeCodeStatusBadge to show current CLI path and allow selection when multiple installations are detected - Add `writeSettingsFile()` to settings-utils for persisting CLI path selection - Add translation keys for new UI elements (English and French) Closes AndyMik90#1001 * fix: address PR review findings for Claude CLI detection Addresses all 8 findings from Auto Claude PR Review: Security improvements: - Add path sanitization (_is_secure_path) to backend CLI validation to prevent command injection via malicious paths - Add isSecurePath validation in frontend IPC handler before CLI execution - Normalize paths with path.resolve() before execution Architecture improvements: - Refactor scanClaudeInstallations to use getClaudeDetectionPaths() from cli-tool-manager.ts as single source of truth (addresses code duplication) - Add cross-reference comments between backend _get_claude_detection_paths() and frontend getClaudeDetectionPaths() to keep them in sync Bug fixes: - Fix path display truncation to use regex /[/\\]/ for cross-platform compatibility (Windows uses backslashes) - Add null check for version in UI rendering (shows "version unknown" instead of "vnull") - Use DEFAULT_APP_SETTINGS merge pattern for settings persistence Debugging improvements: - Add error logging in validateClaudeCliAsync catch block for better debugging of CLI detection issues Translation additions: - Add "versionUnknown" key to English and French navigation.json * ci(release): move VirusTotal scan to separate post-release workflow (AndyMik90#980) * ci(release): move VirusTotal scan to separate post-release workflow VirusTotal scans were blocking release creation, taking 5+ minutes per file. This change moves the scan to a separate workflow that triggers after the release is published, allowing releases to be available immediately. - Create virustotal-scan.yml workflow triggered on release:published - Remove blocking VirusTotal step from release.yml - Scan results are appended to release notes after completion - Add manual trigger option for rescanning old releases * fix(ci): address PR review issues in VirusTotal scan workflow - Add error checking on gh release view to prevent wiping release notes - Replace || true with proper error handling to distinguish "no assets" from real errors - Use file-based approach for release notes to avoid shell expansion issues - Use env var pattern consistently for secret handling - Remove placeholder text before appending VT results - Document 32MB threshold with named constant - Add HTTP status code validation on all curl requests Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(ci): add concurrency control and remove dead code in VirusTotal workflow - Add concurrency group to prevent TOCTOU race condition when multiple workflow_dispatch runs target the same release tag - Remove unused analysis_failed variable declaration Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(ci): improve error handling in VirusTotal workflow - Fail workflow when download errors occur but scannable assets exist - Add explicit timeout handling for analysis polling loop - Use portable sed approach (works on both GNU and BSD sed) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(ui): display actual base branch name instead of hardcoded main (AndyMik90#969) * fix(ui): display actual base branch name instead of hardcoded "main" The merge conflict UI was showing "Main branch has X new commits" regardless of the actual base branch. Now it correctly displays the dynamic branch name (e.g., "develop branch has 40 new commits") using the baseBranch value from gitConflicts. * docs: update README download links to v2.7.3 (AndyMik90#976) - Update all stable download links from 2.7.2 to 2.7.3 - Add Flatpak download link (new in 2.7.3) * fix(i18n): add translation keys for branch divergence messages - Add merge section to taskReview.json with pluralized translations - Update WorkspaceStatus.tsx to use i18n for branch behind message - Update MergePreviewSummary.tsx to use i18n for branch divergence text - Add French translations for all new keys Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(i18n): add missing translation keys for branch behind details - Add branchHasNewCommitsSinceBuild for build started message - Add filesNeedAIMergeDueToRenames for path-mapped files - Add fileRenamesDetected for rename detection message - Add filesRenamedOrMoved for generic rename/move message - Update WorkspaceStatus.tsx to use all new i18n keys Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(i18n): correct pluralization for rename count in AI merge message The filesNeedAIMergeDueToRenames translation has two values that need independent pluralization (fileCount and renameCount). Since i18next only supports one count parameter, added separate translation keys for singular/plural renames and select the correct key based on renameCount value. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(i18n): use translation keys for merge button labels with dynamic branch Replace hardcoded 'Stage to Main' and 'Merge to Main' button labels with i18n translation keys that interpolate the actual target branch name. Also adds translations for loading states (Resolving, Staging, Merging). Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(github-prs): prevent preloading of PRs currently under review (AndyMik90#1006) - Updated logic to skip PRs that are currently being reviewed when determining which PRs need preloading. - Enhanced condition to only fetch existing review data from disk if no review is in progress, ensuring that ongoing reviews are not overwritten by stale data. * chore: bump version to 2.7.4 * hotfix/sentry-backend-build * fix(github): resolve circular import issues in context_gatherer and services (AndyMik90#1026) - Updated import statements in context_gatherer.py to import safe_print from core.io_utils to avoid circular dependencies with the services package. - Introduced lazy imports in services/__init__.py to prevent circular import issues, detailing the import chain in comments for clarity. - Added a lazy import handler to load classes on first access, improving module loading efficiency. * feat(sentry): embed Sentry DSN at build time for packaged apps (AndyMik90#1025) * feat(sentry): integrate Sentry configuration into Electron build - Added build-time constants for Sentry DSN and sampling rates in electron.vite.config.ts. - Enhanced environment variable handling in env-utils.ts to include Sentry settings for subprocesses. - Implemented getSentryEnvForSubprocess function in sentry.ts to provide Sentry environment variables for Python backends. - Updated Sentry-related functions to prioritize build-time constants over runtime environment variables for improved reliability. This integration ensures that Sentry is properly configured for both local development and CI environments. * fix(sentry): add typeof guards for build-time constants in tests The __SENTRY_*__ constants are only defined when Vite's define plugin runs during build. In test environments (vitest), these constants are undefined and cause ReferenceError. Added typeof guards to safely handle both cases. Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * Fix Duplicate Kanban Task Creation on Rapid Button Clicks (AndyMik90#1021) * auto-claude: subtask-1-1 - Add convertingIdeas state and guard logic to useIdeation hook * auto-claude: subtask-1-2 - Update IdeaDetailPanel to accept isConverting prop * auto-claude: subtask-2-1 - Add idempotency check for linked_task_id in task-c * auto-claude: subtask-3-1 - Manual testing: Verify rapid clicking creates only one task - Fixed missing convertingIdeas prop connection in Ideation.tsx - Added convertingIdeas to destructured hook values - Added isConverting prop to IdeaDetailPanel component - Created detailed manual-test-report.md with code review and E2E testing instructions - All code implementation verified via TypeScript checks (no errors) - Multi-layer protection confirmed: UI disabled, guard check, backend idempotency - Manual E2E testing required for final verification Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix: address PR review findings for duplicate task prevention - Fix TOCTOU race condition by moving idempotency check inside lock - Fix React state closure by using ref for synchronous tracking - Add i18n translations for ideation UI (EN + FR) - Add error handling with toast notifications for conversion failures Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Sonnet 4.5 <[email protected]> * feat(terminal): add YOLO mode to invoke Claude with --dangerously-skip-permissions (AndyMik90#1016) * feat(terminal): add YOLO mode to invoke Claude with --dangerously-skip-permissions Add a toggle in Developer Tools settings that enables "YOLO Mode" which starts Claude with the --dangerously-skip-permissions flag, bypassing all safety prompts. Changes: - Add dangerouslySkipPermissions setting to AppSettings interface - Add translation keys for YOLO mode (en/fr) - Modify claude-integration-handler to accept and append extra flags - Update terminal-manager and terminal-handlers to read and forward the setting - Add Switch toggle with warning styling in DevToolsSettings UI The toggle includes visual warnings (amber colors, AlertTriangle icon) to clearly indicate this is a dangerous option that bypasses Claude's permission system. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(terminal): address PR review issues for YOLO mode implementation - Add async readSettingsFileAsync to avoid blocking main process during settings read - Extract YOLO_MODE_FLAG constant to eliminate duplicate flag strings - Store dangerouslySkipPermissions on terminal object to persist YOLO mode across profile switches - Update switchClaudeProfile callback to pass stored YOLO mode setting These fixes address: - LOW: Synchronous file I/O in IPC handler - LOW: Flag string duplicated in invokeClaude and invokeClaudeAsync - MEDIUM: YOLO mode not persisting when switching Claude profiles Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> * Make worktree isolation prominent in UI (AndyMik90#1020) * auto-claude: subtask-1-1 - Add i18n translation keys for worktree notice banner and merge tooltip - Added wizard.worktreeNotice.title and wizard.worktreeNotice.description for task creation banner - Added review.mergeTooltip for merge button explanation - Translations added to both en/tasks.json and fr/tasks.json * auto-claude: subtask-1-2 - Add visible info banner to TaskCreationWizard expl * auto-claude: subtask-1-3 - Add tooltip to 'Merge with AI' button in WorkspaceStatus - Import Tooltip components from ui/tooltip - Wrap merge button with Tooltip, TooltipTrigger, TooltipContent - Add contextual tooltip text explaining merge operation: * With AI: explains worktree merge, removal, and AI conflict resolution * Without AI: explains worktree merge and removal - Follows Radix UI tooltip pattern from reference file * fix: use i18n key for merge button tooltip in WorkspaceStatus * fix: clarify merge tooltip - worktree removal is optional (qa-requested) Fixes misleading tooltip text that implied worktree is automatically removed during merge. In reality, after merge, users are shown a dialog where they can choose to keep or remove the worktree. Updated tooltip to reflect this flow. Changes: - Updated en/tasks.json: Changed tooltip to clarify worktree removal is optional - Updated fr/tasks.json: Updated French translation to match QA Feedback: "Its currently saying on the tooltip that it will 'remove the worktree' Please validate if this is the actual logic. As per my understanding, there will be an extra button afterwards that will make sure that the user has access to the work tree if they want to revert anything. The user has to manually accept to remove the work tree." Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix: use theme-aware colors for worktree info banner Replace hardcoded blue colors with semantic theme classes to support dark mode properly. Uses the same pattern as other info banners in the codebase (bg-info/10, border-info/30, text-info). Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Sonnet 4.5 <[email protected]> * fix(terminal): improve worktree name input UX (AndyMik90#1012) * fix(terminal): improve worktree name input to not strip trailing characters while typing - Allow trailing hyphens/underscores during input (only trim on submit) - Add preview name that shows the final sanitized value for branch preview - Remove invalid characters instead of replacing with hyphens - Collapse consecutive underscores in addition to hyphens - Final sanitization happens on submit to match backend WORKTREE_NAME_REGEX Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(terminal): address PR review findings for worktree name validation - Fix submit button disabled check to use sanitized name instead of raw input - Simplify trailing trim logic (apply once after all transformations) - Apply lowercase in handleNameChange to reduce input/preview gap - Internationalize 'name' fallback using existing translation key Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(terminal): improve header responsiveness for multiple terminals - Hide text labels (Claude, Open in IDE) when ≥4 terminals, show icon only - Add dynamic max-width to worktree name badge with truncation - Add tooltips to all icon-only elements for accessibility - Maintain full functionality while reducing header width requirements Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: Test User <[email protected]> * fix(terminal): enhance terminal recreation logic with retry mechanism (AndyMik90#1013) * fix(terminal): enhance terminal recreation logic with retry mechanism - Introduced a maximum retry limit and delay for terminal recreation when dimensions are not ready. - Added cleanup for retry timers on component unmount to prevent memory leaks. - Improved error handling to report failures after exceeding retry attempts, ensuring better user feedback during terminal setup. * fix(terminal): address PR review feedback for retry mechanism - Fix race condition: clear pending retry timer at START of effect to prevent multiple timers when dependencies change mid-retry - Fix isCreatingRef: keep it true during retry window to prevent duplicate creation attempts from concurrent effect runs - Extract duplicated retry logic into scheduleRetryOrFail helper (consolidated 5 duplicate instances into 1 reusable function) - Add handleSuccess/handleError helpers to reduce code duplication - Reduce file from 295 to 237 lines (~20% reduction) Addresses review feedback from CodeRabbit, Gemini, and Auto Claude. Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Test User <[email protected]> Co-authored-by: Claude Opus 4.5 <[email protected]> * feat(terminal): add task worktrees section and remove terminal limit (AndyMik90#1033) * feat(terminal): add task worktrees section and remove terminal limit - Remove 12 terminal worktree limit (now unlimited) - Add "Task Worktrees" section in worktree dropdown below terminal worktrees - Task worktrees (created by kanban) now accessible for manual work - Update translations for new section labels (EN + FR) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(terminal): address PR review feedback - Clear taskWorktrees state when project is null or changes - Parallelize API calls with Promise.all for better performance - Use consistent path-based filtering for both worktree types - Add clarifying comment for createdAt timestamp Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: Test User <[email protected]> * Add file/screenshot upload to QA feedback interface (AndyMik90#1018) * auto-claude: subtask-1-1 - Add feedbackImages state and handlers to useTaskDetail - Add feedbackImages state as ImageAttachment[] for storing feedback images - Add setFeedbackImages setter for direct state updates - Add addFeedbackImage handler for adding a single image - Add addFeedbackImages handler for adding multiple images at once - Add removeFeedbackImage handler for removing an image by ID - Add clearFeedbackImages handler for clearing all images - Import ImageAttachment type from shared/types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * auto-claude: subtask-1-2 - Update IPC interface to support images in submitReview - Add ImageAttachment import from ./task types - Update submitReview signature to include optional images parameter 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * auto-claude: subtask-1-3 - Update submitReview function in task-store to accept and pass images * auto-claude: subtask-2-1 - Add paste/drop handlers and image thumbnail displa - Add paste event handler for screenshot/image clipboard support - Add drag-over and drag-leave handlers for visual feedback during drag - Add drop handler for image file drops - Add image thumbnail display (64x64) with remove button on hover - Import image utilities from ImageUpload.tsx (generateImageId, blobToBase64, etc.) - Add i18n support for all new UI text - Make new props optional for backward compatibility during incremental rollout - Allow submission with either text feedback or images (not both required) - Add visual drag feedback with border/background color change * auto-claude: subtask-2-2 - Update TaskReview to pass image props to QAFeedbackSection * auto-claude: subtask-2-3 - Update TaskDetailModal to manage image state and pass to TaskReview - Pass feedbackImages and setFeedbackImages from useTaskDetail hook to TaskReview - Update handleReject to include images in submitReview call - Allow submission with images only (no text required) - Clear images after successful submission * auto-claude: subtask-3-1 - Add English translations for feedback image UI * auto-claude: subtask-3-2 - Add French translations for feedback image UI * fix(security): sanitize image filename to prevent path traversal - Use path.basename() to strip directory components from filenames - Validate sanitized filename is not empty, '.', or '..' - Add defense-in-depth check verifying resolved path stays within target directory - Fix base64 data URL regex to handle complex MIME types (e.g., svg+xml) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: add MIME type validation and fix SVG file extension - Add server-side MIME type validation for image uploads (defense in depth) - Fix SVG file extension: map 'image/svg+xml' to '.svg' instead of '.svg+xml' - Add MIME-to-extension mapping for all allowed image types Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: require mimeType and apply SVG extension fix to drop handler - Change MIME validation to reject missing mimeType (prevents bypass) - Add 'image/jpg' to server-side allowlist for consistency - Apply mimeToExtension mapping to drop handler (was only in paste handler) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: Test User <[email protected]> * fix(auth): await profile manager initialization before auth check (AndyMik90#1010) * fix(auth): await profile manager initialization before auth check Fixes race condition where hasValidAuth() was called before the ClaudeProfileManager finished async initialization from disk. The getClaudeProfileManager() returns a singleton immediately with default profile data (no OAuth token). When hasValidAuth() runs before initialization completes, it returns false even when valid credentials exist. Changed all pre-flight auth checks to use await initializeClaudeProfileManager() which ensures initialization completes via promise caching. Signed-off-by: StillKnotKnown <[email protected]> * fix(auth): add error handling for profile manager initialization Prevents unhandled promise rejections when initializeClaudeProfileManager() throws due to filesystem errors (permissions, disk full, corrupt JSON). The ipcMain.on handler for TASK_START doesn't await promises, so unhandled rejections could crash the main process. Wrapped all await initializeClaudeProfileManager() calls in try-catch blocks. Found via automated code review. Signed-off-by: StillKnotKnown <[email protected]> * test: mock initializeClaudeProfileManager in subprocess tests The test mock was only mocking getClaudeProfileManager, but now we also use initializeClaudeProfileManager which wasn't mocked, causing test failures. Signed-off-by: StillKnotKnown <[email protected]> * fix(auth): add try-catch for initializeClaudeProfileManager in remaining handlers Addresses PR review feedback - TASK_UPDATE_STATUS and TASK_RECOVER_STUCK handlers were missing try-catch blocks for initializeClaudeProfileManager(), inconsistent with TASK_START handler. If initialization fails, users now get specific file permissions guidance instead of generic error messages. Signed-off-by: StillKnotKnown <[email protected]> * refactor(auth): extract profile manager initialization into helper Extract the repeated initializeClaudeProfileManager() + try/catch pattern into a helper function ensureProfileManagerInitialized() that returns a discriminated union for type-safe error handling. This reduces code duplication across TASK_START, TASK_UPDATE_STATUS, and TASK_RECOVER_STUCK handlers while preserving context-specific error handling behavior. The helper returns: - { success: true, profileManager } on success - { success: false, error } on failure Signed-off-by: StillKnotKnown <[email protected]> * fix(auth): improve error details and allow retry after transient failures Two improvements to profile manager initialization: 1. Include actual error details in failure response for better debugging. Previously, only a generic message was returned to users, making it hard to diagnose the root cause. Now the error message is appended. 2. Reset cached promise on failure to allow retries after transient errors. Previously, if initialize() failed (e.g., EACCES, ENOSPC), the rejected promise was cached forever, requiring app restart to recover. Now the cached promise is reset on failure, allowing subsequent calls to retry. Signed-off-by: StillKnotKnown <[email protected]> --------- Signed-off-by: StillKnotKnown <[email protected]> Co-authored-by: StillKnotKnown <[email protected]> Co-authored-by: Andy <[email protected]> * fix(frontend): validate Windows claude.cmd reliably in GUI (AndyMik90#1023) * fix: use absolute cmd.exe for Claude CLI validation * fix: make cmd.exe validation type-safe for tests * fix: satisfy frontend typecheck for cli tool tests Signed-off-by: Umaru <[email protected]> * test: mock windows-paths exports for isSecurePath Signed-off-by: Umaru <[email protected]> * test: make cli env tests platform-aware Signed-off-by: Umaru <[email protected]> * test: cover isSecurePath guard in claude detection Signed-off-by: Umaru <[email protected]> * test: align env-utils mocks with shouldUseShell Signed-off-by: Umaru <[email protected]> * test: assert isSecurePath for cmd path * fix(frontend): handle quoted claude.cmd paths in validation --------- Signed-off-by: Umaru <[email protected]> Co-authored-by: Andy <[email protected]> * 2.7.4 release * changelog 2.7.4 --------- Signed-off-by: Black Circle Sentinel <[email protected]> Signed-off-by: StillKnotKnown <[email protected]> Signed-off-by: Umaru <[email protected]> Co-authored-by: StillKnotKnown <[email protected]> Co-authored-by: StillKnotKnown <[email protected]> Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: Michael Ludlow <[email protected]> Co-authored-by: Test User <[email protected]> Co-authored-by: Umaru <[email protected]>
Linux binary installations were missing the `secretstorage` package, causing OAuth token storage via Freedesktop.org Secret Service to fail silently. The build script cache was created before secretstorage was added to requirements.txt (Jan 16, 2026), so the package was not being validated during bundling. Changes: - Add platform-aware critical packages validation in download-python.cjs - Add platform-aware critical packages validation in python-env-manager.ts - Add Linux secretstorage warning in dependency_validator.py - Add comprehensive tests for Linux secretstorage validation The fix follows the same pattern as the Windows pywin32 fix (ACS-306): - Platform-specific packages are only validated on their target platform - Linux: secretstorage (OAuth token storage via keyring) - Windows: pywintypes (MCP library dependency) - macOS: No platform-specific packages The Linux validation emits a warning (not blocking error) since the app gracefully falls back to .env file storage when secretstorage is unavailable. Refs: ACS-310
The tests test_windows_skips_secretstorage_validation and test_macos_skips_secretstorage_validation were failing on Windows CI because they didn't mock the pywintypes import. When running on actual Windows in CI, the pywin32 validation runs first and exits because pywin32 isn't installed in the test environment. The fix mocks pywintypes to succeed so we can properly test that secretstorage validation is skipped on non-Linux platforms.
Changes made based on CodeRabbit AI review: Backend (dependency_validator.py): - Rename _exit_with_secretstorage_warning to _warn_missing_secretstorage to accurately reflect that it doesn't exit (it only emits a warning) - Add sys.stderr.flush() to ensure warning is immediately visible Frontend (download-python.cjs): - Fix critical __init__.py validation logic - packages are now only considered valid if directory+__init__.py exists OR single-file module exists Frontend (python-env-manager.ts): - Use platform abstraction (isWindows(), isLinux()) instead of process.platform - Fix critical packages validation to match download-python.cjs logic Tests (test_dependency_validator.py): - Update function name to _warn_missing_secretstorage - Add is_windows patches to Linux tests to prevent pywin32 validation from running on Windows CI Refs: ACS-310
AndyMik90
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Auto Claude Review - APPROVED
Status: Ready to Merge
Summary: ### Merge Verdict: ✅ READY TO MERGE
✅ Ready to merge - All checks passing, no blocking issues found.
No blocking issues. 2 non-blocking suggestion(s) to consider
Risk Assessment
| Factor | Level | Notes |
|---|---|---|
| Complexity | High | Based on lines changed |
| Security Impact | None | Based on security findings |
| Scope Coherence | Good | Based on structural review |
Findings Summary
- Low: 2 issue(s)
Generated by Auto Claude PR Review
💡 Suggestions (2)
These are non-blocking suggestions for consideration:
🔵 [2772b017d793] [LOW] platformCriticalPackages definition duplicated across 3 locations
📁 apps/frontend/scripts/download-python.cjs:714
The platform-critical packages validation logic is duplicated in: (1) download-python.cjs lines 714-727, (2) download-python.cjs lines 826-839, (3) python-env-manager.ts lines 132-151. Comments acknowledge this with 'Note: Same list exists in python-env-manager.ts - keep them in sync'. While this works correctly, manual synchronization comments are a code smell that increases maintenance burden. The code is documented and functional, so this is a suggestion for future improvement rather than a blocking issue.
Suggested fix:
Consider extracting platformCriticalPackages to a shared config file that both download-python.cjs and python-env-manager.ts can import. Alternatively, extract the validation logic within download-python.cjs into a reusable function to eliminate internal duplication.
🔵 [17c74a3b2398] [LOW] AI comment about createdAt preservation is a false positive
📁 apps/frontend/src/main/terminal/session-handler.ts:165
CodeRabbit flagged that persistSession() overwrites createdAt on every save. However, examining terminal-session-store.ts reveals that saveSession() (lines 189-203) already preserves createdAt for existing sessions - only new sessions get a fresh timestamp. The AI misread the code flow between session-handler.ts and terminal-session-store.ts.
Suggested fix:
No fix needed - this is a false positive. The createdAt is already preserved for existing sessions.
This automated review found no blocking issues. The PR can be safely merged.
Generated by Auto Claude
e8cf42b to
6a709a8
Compare
| const escapedClaudeCmd = escapeShellCommand(claudeCmd); | ||
| const pathPrefix = buildPathPrefix(claudeEnv.PATH || ''); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: The Claude invocation functions call undefined functions like escapeShellCommand and buildPathPrefix, and use an undefined variable escapedTempFile, which will cause a runtime crash.
Severity: CRITICAL
Suggested Fix
Ensure that the helper functions escapeShellCommand and buildPathPrefix are properly defined or imported. Implement the missing logic to create the temporary file and define the escapedTempFile variable before it is used. Also, correct the property name passed to buildClaudeShellCommand from escapedTempFile to tempFile to match the ClaudeCommandConfig type definition.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: apps/frontend/src/main/terminal/claude-integration-handler.ts#L474-L475
Potential issue: The functions `invokeClaude`, `invokeClaudeAsync`, `resumeClaude`, and
`resumeClaudeAsync` will fail at runtime due to multiple `ReferenceError`s. The code
calls the functions `escapeShellCommand` and `buildPathPrefix`, which are not defined or
imported anywhere. Additionally, the variable `escapedTempFile` is used when building
the shell command, but it is never defined because the logic for creating the temporary
token file is missing. These errors appear to be the result of an incomplete refactoring
and will cause any attempt to invoke Claude to crash, making the feature non-functional.
Did we get this right? 👍 / 👎 to inform future reviews.
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | ||
| debugLog('[ClaudeIntegration:invokeClaude] ========== INVOKE CLAUDE COMPLETE (temp file) =========='); | ||
| return; | ||
| } else if (activeProfile.configDir) { |
Check notice
Code scanning / CodeQL
Syntax error
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] ========== INVOKE CLAUDE COMPLETE (temp file) =========='); | ||
| return; | ||
| } else if (activeProfile.configDir) { |
Check notice
Code scanning / CodeQL
Syntax error
| {worktrees.map((wt) => ( | ||
| <DropdownMenuItem | ||
| key={wt.name} | ||
| </> |
Check notice
Code scanning / CodeQL
Syntax error
| {worktrees.map((wt) => ( | ||
| <DropdownMenuItem | ||
| key={wt.name} | ||
| </> |
Check notice
Code scanning / CodeQL
Syntax error
| </> | ||
| )} | ||
| </> | ||
| )} |
Check notice
Code scanning / CodeQL
Syntax error
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | ||
| debugLog('[ClaudeIntegration:invokeClaude] ========== INVOKE CLAUDE COMPLETE (temp file) =========='); | ||
| return; | ||
| } else if (activeProfile.configDir) { |
Check notice
Code scanning / CodeQL
Syntax error Note
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] ========== INVOKE CLAUDE COMPLETE (temp file) =========='); | ||
| return; | ||
| } else if (activeProfile.configDir) { |
Check notice
Code scanning / CodeQL
Syntax error Note
| {worktrees.map((wt) => ( | ||
| <DropdownMenuItem | ||
| key={wt.name} | ||
| </> |
Check notice
Code scanning / CodeQL
Syntax error Note
| {worktrees.map((wt) => ( | ||
| <DropdownMenuItem | ||
| key={wt.name} | ||
| </> |
Check notice
Code scanning / CodeQL
Syntax error Note
| </> | ||
| )} | ||
| </> | ||
| )} |
Check notice
Code scanning / CodeQL
Syntax error Note
| const command = buildClaudeShellCommand(cwdCommand, pathPrefix, escapedClaudeCmd, { method: 'temp-file', escapedTempFile }, extraFlags); | ||
| debugLog('[ClaudeIntegration:invokeClaude] Executing command (temp file method, history-safe)'); | ||
| terminal.pty.write(command); | ||
| profileManager.markProfileUsed(activeProfile.id); | ||
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: The invokeClaude function contains calls to undefined functions and has a broken control flow with a dangling else if statement, which will cause runtime crashes.
Severity: CRITICAL
Suggested Fix
Complete the refactoring of the invokeClaude and invokeClaudeAsync functions. This involves either restoring the deleted helper functions (escapeShellCommand, buildPathPrefix) or replacing their call sites with the correct new logic. The control flow structure must also be fixed by restoring the missing if condition or removing the orphaned else if block to resolve the syntax error.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: apps/frontend/src/main/terminal/claude-integration-handler.ts#L478-L482
Potential issue: An incomplete refactoring in the `invokeClaude` and `invokeClaudeAsync`
functions has resulted in code that is not executable. The functions attempt to call
`escapeShellCommand` and `buildPathPrefix`, which are no longer defined or imported
anywhere in the codebase. This will cause a `ReferenceError` at runtime. Additionally,
the control flow is broken due to an `else if` block that is missing its corresponding
`if` statement, which is a syntax error that will prevent the module from being parsed
and executed. These issues will cause any attempt to use the Claude integration to fail.
Did we get this right? 👍 / 👎 to inform future reviews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (10)
apps/backend/runners/github/services/sdk_utils.py (1)
299-306: Guardresult_previewin DEBUG tool-result path.
result_previewis referenced without assignment in the non-subagent DEBUG_MODE branch.🛠️ Suggested fix
@@ elif DEBUG_MODE: status = "ERROR" if is_error else "OK" safe_print( f"[DEBUG {context_name}] Tool result: {tool_id} [{status}]" ) - if result_preview: - safe_print( - f"[{context_name}] Tool result [{status}]: {result_preview}{'...' if len(str(result_content)) > 100 else ''}" - ) + if result_content: + result_preview = ( + str(result_content)[:100].replace("\n", " ").strip() + ) + if result_preview: + safe_print( + f"[{context_name}] Tool result [{status}]: {result_preview}{'...' if len(str(result_content)) > 100 else ''}" + )apps/frontend/src/renderer/components/terminal/WorktreeSelector.tsx (1)
177-183: Critical: JSX syntax error causes build failure.The pipeline is failing with a parsing error. The JSX structure around the loading state conditional is malformed. Line 181 has a stray
</>closing tag, and the ternary operator structure is broken.Looking at the code, the loading conditional should be:
{isLoading ? ( <div>...loading...</div> ) : ( <>...content...</> )}But the current code has mismatched fragment tags causing the parse error.
🐛 Proposed fix
{isLoading ? ( <div className="flex items-center justify-center py-2"> <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" /> </div> - </> - ) : ( - <> + ) : ( + <> {/* Terminal Worktrees Section */}And ensure the fragment is properly closed before the end of the scrollable div at line 257:
+ </> + )} + </div>apps/backend/core/client.py (1)
27-33: Remove unused import causing pipeline failure.The import
get_claude_detection_paths_structuredis not used anywhere in this file, causing the lint error:F401 imported but unused.🐛 Proposed fix
from core.platform import ( - get_claude_detection_paths_structured, get_comspec_path, is_macos, is_windows, validate_cli_path, )apps/frontend/src/main/terminal/__tests__/claude-integration-handler.test.ts (3)
214-244: Critical: Unclosedtryblock withoutcatch/finally.The test at lines 214-244 has a
tryblock that starts at line 218 but is never closed with acatchorfinallyclause. The closing brace on line 244 appears to end theit()block prematurely.🐛 Proposed fix
it('converts Windows PATH separators to colons for bash invocations', async () => { const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform'); Object.defineProperty(process, 'platform', { value: 'win32' }); try { // ... test code ... expect(profileManager.markProfileUsed).toHaveBeenCalledWith('default'); + } finally { + if (originalPlatform) { + Object.defineProperty(process, 'platform', originalPlatform); + } + } });
77-83: Missing import:isWindowsis used but not imported.The
mockPlatformhelper function usesisWindowsat line 81, but this function is not imported at the top of the file. This will cause aReferenceErrorat runtime.🐛 Proposed fix
Add the missing import at the top of the file:
import { describe, expect, it, vi, beforeEach } from 'vitest'; import type * as pty from '@lydell/node-pty'; import type { TerminalProcess } from '../types'; import { buildCdCommand } from '../../../shared/utils/shell-escape'; +import { isWindows } from '../../platform';
100-109: Missing import:escapeShellArgis used but not imported.The
getQuotedCommandhelper function usesescapeShellArgat line 108, but this function is not imported. This will cause aReferenceErrorat runtime.🐛 Proposed fix
Add the missing import:
-import { buildCdCommand } from '../../../shared/utils/shell-escape'; +import { buildCdCommand, escapeShellArg } from '../../../shared/utils/shell-escape';apps/frontend/src/main/terminal/claude-integration-handler.ts (4)
472-476: Critical: Undefined functionsescapeShellCommandandbuildPathPrefixwill cause runtime crash.The code calls
escapeShellCommand(claudeCmd)andbuildPathPrefix(claudeEnv.PATH || '')but these functions are not defined or imported anywhere in the file. This will cause aReferenceErrorat runtime.Additionally, line 478 uses
escapedTempFilewhich is never defined - the logic to create the temporary token file and assign this variable is missing.🐛 Required actions
- Define or import
escapeShellCommand- likely should beescapeShellArgfromshell-escape.ts- Define or import
buildPathPrefix- this helper needs to be created to generate the PATH prefix for shell commands- Add the missing temp file creation logic that should define
escapedTempFileExample fix for the escape function:
-const escapedClaudeCmd = escapeShellCommand(claudeCmd); +const escapedClaudeCmd = escapeShellArg(claudeCmd);The
buildPathPrefixfunction needs to be defined. Based on the test expectations, it should generate platform-specific PATH prefix strings:function buildPathPrefix(pathValue: string): string { if (!pathValue) return ''; if (isWindows()) { return `set "PATH=${pathValue}" && `; } return `PATH='${normalizePathForBash(pathValue)}' `; }
567-569: Critical:escapeShellCommandis not defined inresumeClaude.Similar to
invokeClaude, theresumeClaudefunction usesescapeShellCommandwhich is not defined or imported.
783-784: Critical:escapeShellCommandandbuildPathPrefixnot defined inresumeClaudeAsync.The async resume function also uses the undefined
escapeShellCommandandbuildPathPrefixfunctions.
85-103: Windows support missing inbuildClaudeShellCommand.The
buildClaudeShellCommandfunction only handles Unix/bash syntax for thetemp-fileandconfig-dirmethods. According to the test file's helper functions and the AI summary, Windows support should use different syntax (e.g.,callfor batch files,clsinstead ofclear, etc.).The function needs platform-conditional logic to support Windows.
🔧 Suggested approach
export function buildClaudeShellCommand( cwdCommand: string, pathPrefix: string, escapedClaudeCmd: string, config: ClaudeCommandConfig, extraFlags?: string ): string { const fullCmd = extraFlags ? `${escapedClaudeCmd}${extraFlags}` : escapedClaudeCmd; if (isWindows()) { switch (config.method) { case 'temp-file': // Windows: call batch file, then delete, then run command return `cls && ${cwdCommand}call "${config.escapedTempFile}" && del "${config.escapedTempFile}" && ${fullCmd}\r`; case 'config-dir': return `cls && ${cwdCommand}set "CLAUDE_CONFIG_DIR=${config.escapedConfigDir}" && ${fullCmd}\r`; default: return `${cwdCommand}${pathPrefix}${fullCmd}\r`; } } // Unix/macOS switch (config.method) { case 'temp-file': return `clear && ${cwdCommand}HISTFILE= HISTCONTROL=ignorespace ${pathPrefix}bash -c "source ${config.escapedTempFile} && rm -f ${config.escapedTempFile} && exec ${fullCmd}"\r`; case 'config-dir': return `clear && ${cwdCommand}HISTFILE= HISTCONTROL=ignorespace CLAUDE_CONFIG_DIR=${config.escapedConfigDir} ${pathPrefix}bash -c "exec ${fullCmd}"\r`; default: return `${cwdCommand}${pathPrefix}${fullCmd}\r`; } }As per coding guidelines, use platform abstraction functions like
isWindows()from the platform module instead of hardcoding platform checks.
🤖 Fix all issues with AI agents
In `@apps/backend/core/client.py`:
- Line 163: Replace local platform checks that assign is_windows =
platform.system() == "Windows" with calls to the platform abstraction
is_windows() function imported from core.platform; specifically update the
occurrences in the module-level code, the _validate_claude_cli() function, and
the find_claude_cli() function to use is_windows() directly (and remove the
unused platform import if there are no other references). Ensure you do not
shadow the imported is_windows symbol with a local variable and keep existing
logic branches intact while switching to is_windows().
- Line 346: Replace the direct platform check platform.system() == "Darwin" with
the imported helper is_macos() from core.platform; locate the conditional in
client.py (the if using platform.system()) and change it to call is_macos() so
the code uses the centralized platform detection logic (ensure is_macos is
imported where used).
In `@apps/backend/security/shell_validators.py`:
- Around line 129-130: Replace the manual split with os.path.basename to
correctly handle Windows backslashes: compute base_cmd =
os.path.basename(first_cmd) (preserving existing behavior when first_cmd has no
separators), then continue using base_cmd for the nested command validation
against SHELL_INTERPRETERS; ensure this change touches the assignment of
base_cmd that currently uses first_cmd.rsplit("/", 1) so Windows paths like
"C:\\Windows\\..." are normalized correctly.
In `@apps/frontend/scripts/download-python.cjs`:
- Around line 825-839: Extract the duplicated package validation into two
helpers: create getCriticalPackages(nodePlatform) that returns the base array
['claude_agent_sdk','dotenv','pydantic_core'] merged with
platformCriticalPackages (use the existing platformCriticalPackages mapping),
and create findMissingPackages(packages, sitePackagesDir) that implements the
filter logic used to compute postInstallMissing (checking pkgPath + __init__.py
and pkg + '.py'). Replace the two duplicated blocks that build criticalPackages
and postInstallMissing with calls to getCriticalPackages(info.nodePlatform) and
findMissingPackages(criticalPackages, sitePackagesDir), keeping the original
variable names (criticalPackages, postInstallMissing) for compatibility.
In `@apps/frontend/src/__tests__/integration/terminal-copy-paste.test.ts`:
- Around line 80-85: The test overrides global.requestAnimationFrame with a
vi.fn which can leak into other tests; capture the original (e.g. const
originalRaf = global.requestAnimationFrame) before assigning the mock in the
test file and restore it in an afterEach block (using
global.requestAnimationFrame = originalRaf) so the global is reset after each
test; apply this change for both places where requestAnimationFrame is mocked
(the blocks that use vi.fn and callback.call) and ensure the afterEach runs even
if tests fail.
In
`@apps/frontend/src/main/terminal/__tests__/claude-integration-handler.test.ts`:
- Around line 541-756: There are duplicated, hardcoded Unix-style tests that
replicate cases already covered by the parameterized
describe.each(['win32','darwin','linux']) block; remove the duplicate it(...)
blocks whose titles include "uses the temp token flow when the active profile
has an oauth token", "prefers the temp token flow when profile has both oauth
token and config dir", "handles missing profiles by falling back to the default
command", "uses the config dir flow when the active profile has a config dir",
"uses profile switching when a non-default profile is requested", and "uses
--continue regardless of sessionId (sessionId is deprecated)"; delete these
duplicated tests (the blocks that call invokeClaude and resumeClaude and assert
terminal writes) so only the parameterized describe.each tests remain and avoid
name conflicts and platform assumptions.
- Around line 199-212: Test code invoking invokeClaude and asserting
side-effects is placed outside any it()/test() block and references an undefined
profileManager; wrap the async invocation and assertions in an it(...) async
test block (e.g., it('invokes Claude and persists session', async () => { ...
})) so the top-level await runs inside an async function, and ensure
profileManager is available in the test scope (import or mock profileManager
used in the assertions, or reference the existing mocked instance used elsewhere
in this file). Locate the invokeClaude call, createMockTerminal usage,
written/expect assertions, and the
mockReleaseSessionId/mockPersistSession/profileManager.getActiveProfile/profileManager.markProfileUsed
references to move into the new it() block and adjust imports/mocks accordingly.
In `@apps/frontend/src/main/terminal/claude-integration-handler.ts`:
- Around line 630-643: The block that sets up Claude mode and YOLO flags is
duplicated in invokeClaudeAsync (the pre-try block and again inside the try),
mirroring invokeClaude; remove the redundant pre-try initialization so the code
only sets extraFlags = dangerouslySkipPermissions ? YOLO_MODE_FLAG : undefined
and updates terminal.isClaudeMode, terminal.dangerouslySkipPermissions, calls
SessionHandler.releaseSessionId(terminal.id), and clears
terminal.claudeSessionId once — keep that single initialization where the try
block currently performs it (or move the unique one outside the try and delete
the duplicate inside) to avoid double-execution and ensure only one source of
truth for the YOLO_MODE_FLAG and terminal state.
- Around line 684-702: In invokeClaudeAsync there’s a mismatched/ orphaned "else
if" and duplicated unreachable post-catch block; fix by correcting the
surrounding try/catch/if brace structure so the "else if
(activeProfile.configDir)" branches attach to the original if chain, remove the
duplicated terminal invocation/finalize block after the catch, and consolidate
the terminal.pty.write, profileManager.markProfileUsed, and finalizeClaudeInvoke
calls into the single correct branch path (functions/idents to check:
invokeClaudeAsync, buildClaudeShellCommand, terminal.pty.write,
profileManager.markProfileUsed, finalizeClaudeInvoke) so control flow
returns/continues correctly without parse errors or dead code.
- Around line 478-496: Restore the proper if/else-if/else conditional chain
around the temp-file/config-dir/default branches so the orphaned "else if
(activeProfile.configDir)" is attached to the initial token check: wrap the
temp-file branch in if (activeProfile?.oauthToken) { ... } then use else if
(activeProfile?.configDir) { buildClaudeShellCommand(..., { method:
'config-dir', escapedConfigDir }, ...); terminal.pty.write(...);
profileManager.markProfileUsed(activeProfile.id); finalizeClaudeInvoke(...); }
else { debugLog('[ClaudeIntegration:invokeClaude] WARNING: No token or configDir
available for non-default profile'); } — ensure you reference the same symbols
(activeProfile, buildClaudeShellCommand, terminal.pty.write,
profileManager.markProfileUsed, finalizeClaudeInvoke) and keep the existing
debugLog/return behavior inside each branch so the syntax error is resolved.
- Around line 95-98: The ClaudeCommandConfig union is missing the escaped
property names used in command construction: update the type for the 'temp-file'
and 'config-dir' variants in ClaudeCommandConfig to include escapedTempFile:
string and escapedConfigDir: string respectively (keeping tempFile/configDir if
still needed), so the usages of config.escapedTempFile and
config.escapedConfigDir in the command-building code (where fullCmd is
assembled) match the type definition.
- Around line 527-541: Remove the unreachable duplicate block that appears after
the `throw error` in the catch path: delete the repeated calls to
buildClaudeShellCommand(..., { method: 'default' }, ...),
terminal.pty.write(command), profileManager.markProfileUsed(...),
finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow,
onSessionCapture), and the trailing debugLog('[ClaudeIntegration:invokeClaude]
========== INVOKE CLAUDE COMPLETE (default) =========='), leaving only the
original working invocation logic earlier in the function (the code paths around
buildClaudeShellCommand, terminal.pty.write, profileManager.markProfileUsed, and
finalizeClaudeInvoke) so there is no duplicate/unreachable code after the `throw
error`.
In `@apps/frontend/src/renderer/components/terminal/WorktreeSelector.tsx`:
- Around line 210-218: The delete button in WorktreeSelector (the button that
calls setDeleteWorktree(wt) inside the dropdown) lacks an explicit type and will
default to submit; update the button element to include type="button" to prevent
accidental form submissions—locate the button with the onClick handler (calling
setDeleteWorktree) and add the type attribute while keeping existing props and
handlers intact.
♻️ Duplicate comments (4)
apps/backend/runners/github/services/sdk_utils.py (2)
185-201: Restore progress logging state initialization to avoid NameError.
last_progress_logandPROGRESS_LOG_INTERVALare referenced without initialization, which will crash on the first message.🛠️ Suggested fix
@@ async def process_sdk_stream( - result_text = "" + result_text = "" structured_output = None agents_invoked = [] msg_count = 0 stream_error = None + # Progress logging state (restore prior defaults) + PROGRESS_LOG_INTERVAL = 25 + last_progress_log = 0
327-337: Definemodel_infoand remove duplicate “Invoking agent” log.
model_infois undefined and will raiseNameError; the log is also duplicated.🛠️ Suggested fix
@@ async def process_sdk_stream( - result_text = "" + result_text = "" structured_output = None agents_invoked = [] msg_count = 0 stream_error = None + model_info = f" ({_short_model_name(model)})" if model else "" @@ - safe_print( - f"[{context_name}] Invoking agent: {agent_name}" - ) - safe_print( - f"[{context_name}] Invoking agent: {agent_name}{model_info}" - ) + safe_print( + f"[{context_name}] Invoking agent: {agent_name}{model_info}" + )tests/test_dependency_validator.py (1)
236-256: Missing explicitis_windowsmock for deterministic behavior.This test mocks
is_linuxtoFalsebut doesn't explicitly mockis_windows. While the test happens to work becausemock_importreturns aMagicMockforpywintypes, it's inconsistent with the other tests and could behave unexpectedly in edge cases.For clarity and consistency with
test_macos_skips_secretstorage_validation(lines 270-271), explicitly mockis_windowstoTrue.♻️ Proposed fix
def test_windows_skips_secretstorage_validation(self): """Windows should skip secretstorage validation.""" import builtins original_import = builtins.__import__ def mock_import(name, *args, **kwargs): # Allow pywintypes to succeed (Windows validation passes) if name == "pywintypes": return MagicMock() # secretstorage import fails (but should be skipped on Windows) if name == "secretstorage": raise ImportError("No module named 'secretstorage'") return original_import(name, *args, **kwargs) - with patch("core.dependency_validator.is_linux", return_value=False), \ + with patch("core.dependency_validator.is_windows", return_value=True), \ + patch("core.dependency_validator.is_linux", return_value=False), \ patch("core.dependency_validator._warn_missing_secretstorage") as mock_warning, \ patch("builtins.__import__", side_effect=mock_import): # Should not call warning function validate_platform_dependencies() mock_warning.assert_not_called()Based on learnings, tests for platform-specific code should mock platform detection functions to test all three platforms deterministically.
apps/frontend/src/main/terminal/claude-integration-handler.ts (1)
429-441: Critical: Duplicate initialization code outside try block.Lines 429-441 contain initialization code that is duplicated inside the try block (lines 448-460). This appears to be an incomplete refactoring. The code at lines 429-441 should be removed as it's duplicated and creates variable scoping issues.
| """ | ||
| return get_claude_detection_paths_structured() | ||
| home_dir = Path.home() | ||
| is_windows = platform.system() == "Windows" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Inconsistent platform detection pattern - local variables shadow imported functions.
Multiple places create local is_windows variables using platform.system() == "Windows", which shadows the imported is_windows function from core.platform. This violates the coding guideline to use platform abstraction functions and creates confusion.
♻️ Proposed fix
Use the imported is_windows() function directly instead of creating local variables:
def _get_claude_detection_paths() -> dict[str, list[str]]:
...
home_dir = Path.home()
- is_windows = platform.system() == "Windows"
-
- ...
-
- if is_windows:
+ if is_windows():
platform_paths = [Apply similar changes to _validate_claude_cli() (line 248) and find_claude_cli() (line 320).
This also allows removing import platform from line 19 if no other usages remain.
As per coding guidelines: "Do not check process.platform directly in code - always import platform detection functions from the platform abstraction module."
Also applies to: 248-248, 320-320
🤖 Prompt for AI Agents
In `@apps/backend/core/client.py` at line 163, Replace local platform checks that
assign is_windows = platform.system() == "Windows" with calls to the platform
abstraction is_windows() function imported from core.platform; specifically
update the occurrences in the module-level code, the _validate_claude_cli()
function, and the find_claude_cli() function to use is_windows() directly (and
remove the unused platform import if there are no other references). Ensure you
do not shadow the imported is_windows symbol with a local variable and keep
existing logic branches intact while switching to is_windows().
|
|
||
| # 3. Homebrew paths (macOS) | ||
| if is_macos(): | ||
| if platform.system() == "Darwin": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Use is_macos() instead of direct platform check.
This line uses platform.system() == "Darwin" directly instead of the imported is_macos() function from core.platform.
♻️ Proposed fix
# 3. Homebrew paths (macOS)
- if platform.system() == "Darwin":
+ if is_macos():
for hb_path in paths["homebrew"]:📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if platform.system() == "Darwin": | |
| # 3. Homebrew paths (macOS) | |
| if is_macos(): | |
| for hb_path in paths["homebrew"]: |
🤖 Prompt for AI Agents
In `@apps/backend/core/client.py` at line 346, Replace the direct platform check
platform.system() == "Darwin" with the imported helper is_macos() from
core.platform; locate the conditional in client.py (the if using
platform.system()) and change it to call is_macos() so the code uses the
centralized platform detection logic (ensure is_macos is imported where used).
| # Handle paths like /bin/bash | ||
| base_cmd = first_cmd.rsplit("/", 1)[-1] if "/" in first_cmd else first_cmd |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cross-platform path handling broken — security bypass on Windows.
The simple / split does not handle Windows backslash paths. A command like C:\Windows\System32\bash.exe -c "malicious" would yield base_cmd = "C:\\Windows\\System32\\bash.exe" which won't match SHELL_INTERPRETERS, bypassing nested command validation entirely.
The removed _cross_platform_basename presumably handled both separators. Per coding guidelines, use the platform abstraction module for path handling.
Proposed fix using os.path.basename
- # Handle paths like /bin/bash
- base_cmd = first_cmd.rsplit("/", 1)[-1] if "/" in first_cmd else first_cmd
+ # Handle paths like /bin/bash or C:\Windows\System32\bash.exe
+ base_cmd = os.path.basename(first_cmd)os.path.basename handles both Unix and Windows path separators correctly and is already available since os is imported at line 13.
🤖 Prompt for AI Agents
In `@apps/backend/security/shell_validators.py` around lines 129 - 130, Replace
the manual split with os.path.basename to correctly handle Windows backslashes:
compute base_cmd = os.path.basename(first_cmd) (preserving existing behavior
when first_cmd has no separators), then continue using base_cmd for the nested
command validation against SHELL_INTERPRETERS; ensure this change touches the
assignment of base_cmd that currently uses first_cmd.rsplit("/", 1) so Windows
paths like "C:\\Windows\\..." are normalized correctly.
| // secretstorage is platform-critical for Linux (ACS-310) - required for OAuth token storage | ||
| const platformCriticalPackages = { | ||
| 'win32': ['pywintypes'], // Check for 'pywintypes' instead of 'pywin32' (pywin32 installs top-level modules) | ||
| 'linux': ['secretstorage'] // Linux OAuth token storage via Freedesktop.org Secret Service | ||
| }; | ||
| const criticalPackages = ['claude_agent_sdk', 'dotenv', 'pydantic_core'] | ||
| .concat(info.nodePlatform === 'win32' ? ['pywintypes'] : []); | ||
| .concat(platformCriticalPackages[info.nodePlatform] || []); | ||
| const postInstallMissing = criticalPackages.filter(pkg => { | ||
| const pkgPath = path.join(sitePackagesDir, pkg); | ||
| const initFile = path.join(pkgPath, '__init__.py'); | ||
| const initPath = path.join(pkgPath, '__init__.py'); | ||
| // For single-file modules (like pywintypes.py), check for the file directly | ||
| const moduleFile = path.join(sitePackagesDir, pkg + '.py'); | ||
| // Package is valid if directory+__init__.py exists OR single-file module exists | ||
| return !fs.existsSync(initFile) && !fs.existsSync(moduleFile); | ||
| return !(fs.existsSync(pkgPath) && fs.existsSync(initPath)) && !fs.existsSync(moduleFile); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider extracting duplicated validation logic into a helper function.
This block (Lines 825-839) is identical to the earlier block at Lines 713-727. Both define the same platformCriticalPackages, construct criticalPackages identically, and use the same validation filter. This duplication creates maintenance risk — if one is updated, the other may be forgotten.
♻️ Suggested refactor
Extract the shared logic to a helper function at the top of the file:
/**
* Get platform-critical packages for validation.
* `@param` {string} nodePlatform - Target platform (darwin, win32, linux)
* `@returns` {string[]} Array of critical package names
*/
function getCriticalPackages(nodePlatform) {
const platformCriticalPackages = {
'win32': ['pywintypes'],
'linux': ['secretstorage']
};
return ['claude_agent_sdk', 'dotenv', 'pydantic_core']
.concat(platformCriticalPackages[nodePlatform] || []);
}
/**
* Check for missing critical packages in site-packages directory.
* `@param` {string[]} packages - Package names to check
* `@param` {string} sitePackagesDir - Path to site-packages
* `@returns` {string[]} Array of missing package names
*/
function findMissingPackages(packages, sitePackagesDir) {
return packages.filter(pkg => {
const pkgPath = path.join(sitePackagesDir, pkg);
const initPath = path.join(pkgPath, '__init__.py');
const moduleFile = path.join(sitePackagesDir, pkg + '.py');
return !(fs.existsSync(pkgPath) && fs.existsSync(initPath)) && !fs.existsSync(moduleFile);
});
}Then use at both locations:
const criticalPackages = getCriticalPackages(info.nodePlatform);
const missingPackages = findMissingPackages(criticalPackages, sitePackagesDir);🤖 Prompt for AI Agents
In `@apps/frontend/scripts/download-python.cjs` around lines 825 - 839, Extract
the duplicated package validation into two helpers: create
getCriticalPackages(nodePlatform) that returns the base array
['claude_agent_sdk','dotenv','pydantic_core'] merged with
platformCriticalPackages (use the existing platformCriticalPackages mapping),
and create findMissingPackages(packages, sitePackagesDir) that implements the
filter logic used to compute postInstallMissing (checking pkgPath + __init__.py
and pkg + '.py'). Replace the two duplicated blocks that build criticalPackages
and postInstallMissing with calls to getCriticalPackages(info.nodePlatform) and
findMissingPackages(criticalPackages, sitePackagesDir), keeping the original
variable names (criticalPackages, postInstallMissing) for compatibility.
| // Mock requestAnimationFrame for xterm.js integration tests | ||
| global.requestAnimationFrame = vi.fn((callback: FrameRequestCallback) => { | ||
| // Synchronously execute the callback to avoid timing issues in tests | ||
| callback.call(window, 0); | ||
| return 0; | ||
| }) as unknown as Mock; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restore requestAnimationFrame after each test to avoid global pollution.
Overriding global.requestAnimationFrame without restoring it can leak state into other test files. Capture the original and restore it in afterEach.
♻️ Suggested fix
@@
describe('Terminal copy/paste integration', () => {
+ let originalRequestAnimationFrame: typeof global.requestAnimationFrame | undefined;
@@
beforeEach(() => {
vi.clearAllMocks();
@@
- global.requestAnimationFrame = vi.fn((callback: FrameRequestCallback) => {
+ originalRequestAnimationFrame = global.requestAnimationFrame;
+ global.requestAnimationFrame = vi.fn((callback: FrameRequestCallback) => {
// Synchronously execute the callback to avoid timing issues in tests
callback.call(window, 0);
return 0;
}) as unknown as Mock;
});
@@
afterEach(() => {
+ if (originalRequestAnimationFrame) {
+ global.requestAnimationFrame = originalRequestAnimationFrame;
+ }
vi.restoreAllMocks();
});Also applies to: 104-106
🤖 Prompt for AI Agents
In `@apps/frontend/src/__tests__/integration/terminal-copy-paste.test.ts` around
lines 80 - 85, The test overrides global.requestAnimationFrame with a vi.fn
which can leak into other tests; capture the original (e.g. const originalRaf =
global.requestAnimationFrame) before assigning the mock in the test file and
restore it in an afterEach block (using global.requestAnimationFrame =
originalRaf) so the global is reset after each test; apply this change for both
places where requestAnimationFrame is mocked (the blocks that use vi.fn and
callback.call) and ensure the afterEach runs even if tests fail.
| const command = buildClaudeShellCommand(cwdCommand, pathPrefix, escapedClaudeCmd, { method: 'temp-file', escapedTempFile }, extraFlags); | ||
| debugLog('[ClaudeIntegration:invokeClaude] Executing command (temp file method, history-safe)'); | ||
| terminal.pty.write(command); | ||
| profileManager.markProfileUsed(activeProfile.id); | ||
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | ||
| debugLog('[ClaudeIntegration:invokeClaude] ========== INVOKE CLAUDE COMPLETE (temp file) =========='); | ||
| return; | ||
| } else if (activeProfile.configDir) { | ||
| const escapedConfigDir = escapeShellArg(activeProfile.configDir); | ||
| const command = buildClaudeShellCommand(cwdCommand, pathPrefix, escapedClaudeCmd, { method: 'config-dir', escapedConfigDir }, extraFlags); | ||
| debugLog('[ClaudeIntegration:invokeClaude] Executing command (configDir method, history-safe)'); | ||
| terminal.pty.write(command); | ||
| profileManager.markProfileUsed(activeProfile.id); | ||
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | ||
| debugLog('[ClaudeIntegration:invokeClaude] ========== INVOKE CLAUDE COMPLETE (configDir) =========='); | ||
| return; | ||
| } else { | ||
| debugLog('[ClaudeIntegration:invokeClaude] WARNING: No token or configDir available for non-default profile'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Parse error - orphaned else if clause without matching if/try.
Lines 485-496 contain an else if (activeProfile.configDir) block that appears without a preceding if statement or proper try-catch structure. This is a syntax error that prevents compilation.
The pipeline failure confirms: Parsing error: 'catch' or 'finally' expected.
Looking at the intended logic, this should be part of a conditional chain checking for OAuth token first, then config directory. The structure is broken.
🐛 Analysis of the structural issue
The code structure should be:
if (activeProfile?.oauthToken) {
// temp-file method
} else if (activeProfile?.configDir) {
// config-dir method
} else {
// default method
}But the current code has:
- A broken
ifcondition that was likely removed - An orphaned
else ifat line 485 - Code that falls through incorrectly
This entire section (lines 478-511) needs to be rewritten with proper control flow.
🧰 Tools
🪛 Biome (2.1.2)
[error] 485-485: Expected a catch clause but instead found 'else'.
Expected a catch clause here.
(parse)
🪛 GitHub Actions: Lint
[error] 485-485: Parsing error: 'catch' or 'finally' expected
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/terminal/claude-integration-handler.ts` around lines
478 - 496, Restore the proper if/else-if/else conditional chain around the
temp-file/config-dir/default branches so the orphaned "else if
(activeProfile.configDir)" is attached to the initial token check: wrap the
temp-file branch in if (activeProfile?.oauthToken) { ... } then use else if
(activeProfile?.configDir) { buildClaudeShellCommand(..., { method:
'config-dir', escapedConfigDir }, ...); terminal.pty.write(...);
profileManager.markProfileUsed(activeProfile.id); finalizeClaudeInvoke(...); }
else { debugLog('[ClaudeIntegration:invokeClaude] WARNING: No token or configDir
available for non-default profile'); } — ensure you reference the same symbols
(activeProfile, buildClaudeShellCommand, terminal.pty.write,
profileManager.markProfileUsed, finalizeClaudeInvoke) and keep the existing
debugLog/return behavior inside each branch so the syntax error is resolved.
|
|
||
| if (activeProfile && !activeProfile.isDefault) { | ||
| debugLog('[ClaudeIntegration:invokeClaude] Using terminal environment for non-default profile:', activeProfile.name); | ||
| } | ||
|
|
||
| const command = buildClaudeShellCommand(cwdCommand, pathPrefix, escapedClaudeCmd, { method: 'default' }, extraFlags); | ||
| debugLog('[ClaudeIntegration:invokeClaude] Executing command (default method):', command); | ||
| terminal.pty.write(command); | ||
|
|
||
| if (activeProfile) { | ||
| profileManager.markProfileUsed(activeProfile.id); | ||
| } | ||
|
|
||
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | ||
| debugLog('[ClaudeIntegration:invokeClaude] ========== INVOKE CLAUDE COMPLETE (default) =========='); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Unreachable duplicate code after throw statement.
Lines 527-541 contain code that duplicates lines 498-511, placed after the throw error statement in the catch block. This code is unreachable and appears to be a merge conflict artifact or incomplete refactoring.
🗑️ Proposed fix
Remove the duplicate unreachable code:
throw error; // Re-throw to allow caller to handle
}
-
- if (activeProfile && !activeProfile.isDefault) {
- debugLog('[ClaudeIntegration:invokeClaude] Using terminal environment for non-default profile:', activeProfile.name);
- }
-
- const command = buildClaudeShellCommand(cwdCommand, pathPrefix, escapedClaudeCmd, { method: 'default' }, extraFlags);
- debugLog('[ClaudeIntegration:invokeClaude] Executing command (default method):', command);
- terminal.pty.write(command);
-
- if (activeProfile) {
- profileManager.markProfileUsed(activeProfile.id);
- }
-
- finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture);
- debugLog('[ClaudeIntegration:invokeClaude] ========== INVOKE CLAUDE COMPLETE (default) ==========');
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (activeProfile && !activeProfile.isDefault) { | |
| debugLog('[ClaudeIntegration:invokeClaude] Using terminal environment for non-default profile:', activeProfile.name); | |
| } | |
| const command = buildClaudeShellCommand(cwdCommand, pathPrefix, escapedClaudeCmd, { method: 'default' }, extraFlags); | |
| debugLog('[ClaudeIntegration:invokeClaude] Executing command (default method):', command); | |
| terminal.pty.write(command); | |
| if (activeProfile) { | |
| profileManager.markProfileUsed(activeProfile.id); | |
| } | |
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | |
| debugLog('[ClaudeIntegration:invokeClaude] ========== INVOKE CLAUDE COMPLETE (default) =========='); |
🧰 Tools
🪛 Biome (2.1.2)
[error] 533-533: This variable is used before its declaration.
The variable is declared here:
(lint/correctness/noInvalidUseBeforeDeclaration)
[error] 534-534: This variable is used before its declaration.
The variable is declared here:
(lint/correctness/noInvalidUseBeforeDeclaration)
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/terminal/claude-integration-handler.ts` around lines
527 - 541, Remove the unreachable duplicate block that appears after the `throw
error` in the catch path: delete the repeated calls to
buildClaudeShellCommand(..., { method: 'default' }, ...),
terminal.pty.write(command), profileManager.markProfileUsed(...),
finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow,
onSessionCapture), and the trailing debugLog('[ClaudeIntegration:invokeClaude]
========== INVOKE CLAUDE COMPLETE (default) =========='), leaving only the
original working invocation logic earlier in the function (the code paths around
buildClaudeShellCommand, terminal.pty.write, profileManager.markProfileUsed, and
finalizeClaudeInvoke) so there is no duplicate/unreachable code after the `throw
error`.
| debugLog('[ClaudeIntegration:invokeClaudeAsync] ========== INVOKE CLAUDE START (async) =========='); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] Terminal ID:', terminal.id); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] Requested profile ID:', profileId); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] CWD:', cwd); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] Dangerously skip permissions:', dangerouslySkipPermissions); | ||
|
|
||
| // Compute extra flags for YOLO mode | ||
| const extraFlags = dangerouslySkipPermissions ? YOLO_MODE_FLAG : undefined; | ||
|
|
||
| terminal.isClaudeMode = true; | ||
| // Store YOLO mode setting so it persists across profile switches | ||
| terminal.dangerouslySkipPermissions = dangerouslySkipPermissions; | ||
| SessionHandler.releaseSessionId(terminal.id); | ||
| terminal.claudeSessionId = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Duplicate initialization in invokeClaudeAsync before try block.
Lines 630-643 contain initialization code that is duplicated inside the try block (lines 648-661). This mirrors the same issue in invokeClaude.
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/terminal/claude-integration-handler.ts` around lines
630 - 643, The block that sets up Claude mode and YOLO flags is duplicated in
invokeClaudeAsync (the pre-try block and again inside the try), mirroring
invokeClaude; remove the redundant pre-try initialization so the code only sets
extraFlags = dangerouslySkipPermissions ? YOLO_MODE_FLAG : undefined and updates
terminal.isClaudeMode, terminal.dangerouslySkipPermissions, calls
SessionHandler.releaseSessionId(terminal.id), and clears
terminal.claudeSessionId once — keep that single initialization where the try
block currently performs it (or move the unique one outside the try and delete
the duplicate inside) to avoid double-execution and ensure only one source of
truth for the YOLO_MODE_FLAG and terminal state.
| const command = buildClaudeShellCommand(cwdCommand, pathPrefix, escapedClaudeCmd, { method: 'temp-file', escapedTempFile }, extraFlags); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] Executing command (temp file method, history-safe)'); | ||
| terminal.pty.write(command); | ||
| profileManager.markProfileUsed(activeProfile.id); | ||
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] ========== INVOKE CLAUDE COMPLETE (temp file) =========='); | ||
| return; | ||
| } else if (activeProfile.configDir) { | ||
| const escapedConfigDir = escapeShellArg(activeProfile.configDir); | ||
| const command = buildClaudeShellCommand(cwdCommand, pathPrefix, escapedClaudeCmd, { method: 'config-dir', escapedConfigDir }, extraFlags); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] Executing command (configDir method, history-safe)'); | ||
| terminal.pty.write(command); | ||
| profileManager.markProfileUsed(activeProfile.id); | ||
| finalizeClaudeInvoke(terminal, activeProfile, projectPath, startTime, getWindow, onSessionCapture); | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] ========== INVOKE CLAUDE COMPLETE (configDir) =========='); | ||
| return; | ||
| } else { | ||
| debugLog('[ClaudeIntegration:invokeClaudeAsync] WARNING: No token or configDir available for non-default profile'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Same structural issues in invokeClaudeAsync - orphaned else and duplicate code.
The async version has the same critical issues:
- Line 691: Orphaned
else ifclause (parse error) - Lines 735-749: Unreachable duplicate code after catch block
Also applies to: 735-749
🧰 Tools
🪛 Biome (2.1.2)
[error] 691-691: Expected a catch clause but instead found 'else'.
Expected a catch clause here.
(parse)
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/terminal/claude-integration-handler.ts` around lines
684 - 702, In invokeClaudeAsync there’s a mismatched/ orphaned "else if" and
duplicated unreachable post-catch block; fix by correcting the surrounding
try/catch/if brace structure so the "else if (activeProfile.configDir)" branches
attach to the original if chain, remove the duplicated terminal
invocation/finalize block after the catch, and consolidate the
terminal.pty.write, profileManager.markProfileUsed, and finalizeClaudeInvoke
calls into the single correct branch path (functions/idents to check:
invokeClaudeAsync, buildClaudeShellCommand, terminal.pty.write,
profileManager.markProfileUsed, finalizeClaudeInvoke) so control flow
returns/continues correctly without parse errors or dead code.
| <button | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| setIsOpen(false); | ||
| onSelectWorktree(wt); | ||
| e.preventDefault(); | ||
| setDeleteWorktree(wt); | ||
| }} | ||
| className="text-xs group" | ||
| className="ml-2 p-1 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive opacity-0 group-hover:opacity-100 transition-opacity shrink-0" | ||
| title={t('common:delete')} | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add explicit type="button" to prevent unintended form submission.
The delete button inside the dropdown menu item is missing an explicit type attribute. The default type is submit, which could cause unintended form submissions if this component is ever placed inside a form element.
🔧 Proposed fix
<button
+ type="button"
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
setDeleteWorktree(wt);
}}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button | |
| onClick={(e) => { | |
| e.stopPropagation(); | |
| setIsOpen(false); | |
| onSelectWorktree(wt); | |
| e.preventDefault(); | |
| setDeleteWorktree(wt); | |
| }} | |
| className="text-xs group" | |
| className="ml-2 p-1 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive opacity-0 group-hover:opacity-100 transition-opacity shrink-0" | |
| title={t('common:delete')} | |
| > | |
| <button | |
| type="button" | |
| onClick={(e) => { | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| setDeleteWorktree(wt); | |
| }} | |
| className="ml-2 p-1 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive opacity-0 group-hover:opacity-100 transition-opacity shrink-0" | |
| title={t('common:delete')} | |
| > |
🧰 Tools
🪛 Biome (2.1.2)
[error] 210-218: Provide an explicit type prop for the button element.
The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset
(lint/a11y/useButtonType)
🤖 Prompt for AI Agents
In `@apps/frontend/src/renderer/components/terminal/WorktreeSelector.tsx` around
lines 210 - 218, The delete button in WorktreeSelector (the button that calls
setDeleteWorktree(wt) inside the dropdown) lacks an explicit type and will
default to submit; update the button element to include type="button" to prevent
accidental form submissions—locate the button with the onClick handler (calling
setDeleteWorktree) and add the type attribute while keeping existing props and
handlers intact.
|
pr broken |
Summary
Linux binary installations were missing the
secretstoragepackage, causing OAuth token storage via Freedesktop.org Secret Service (gnome-keyring/kwallet) to fail silently. The build script cache was created beforesecretstoragewas added to requirements.txt (January 16, 2026), resulting in Linux binaries shipping without the required package.Root Cause
The Linux binary build process caches Python dependencies in
apps/frontend/python-runtime. When the.bundledmarker file exists, the build script skips reinstallation. The cache was created beforesecretstoragewas added to requirements.txt, and the validation only checked for hardcoded "critical packages" (claude_agent_sdk,dotenv,pydantic_core) which did not includesecretstorage.Changes
Build System
Backend
secretstoragevalidation with helpful warning messageTests
Pattern
This fix follows the same pattern as the Windows pywin32 fix (ACS-306):
secretstorage(OAuth token storage via keyring)pywintypes(MCP library dependency)Behavior
The Linux validation emits a warning (not a blocking error) since the app gracefully falls back to .env file storage when
secretstorageis unavailable. This is intentional becausesecretstorageis optional for app functionality, unlikepywin32which is required on Windows.Testing
Related
Type of Change
Checklist
Screenshots
N/A (backend/build system change)
Breaking Changes
None
Summary by CodeRabbit
Release Notes
Bug Fixes
New Features
UI Improvements
✏️ Tip: You can customize this high-level summary in your review settings.