Files
claude-mem/docs/reports/issue-590-windows-chroma-terminal-popup.md
T
Alex Newman 2659ec3231 fix: Claude Code 2.1.1 compatibility + log-level audit + path validation fixes (#614)
* Refactor CLAUDE.md and related files for December 2025 updates

- Updated CLAUDE.md in src/services/worker with new entries for December 2025, including changes to Search.ts, GeminiAgent.ts, SDKAgent.ts, and SessionManager.ts.
- Revised CLAUDE.md in src/shared to reflect updates and new entries for December 2025, including paths.ts and worker-utils.ts.
- Modified hook-constants.ts to clarify exit codes and their behaviors.
- Added comprehensive hooks reference documentation for Claude Code, detailing usage, events, and examples.
- Created initial CLAUDE.md files in various directories to track recent activity.

* fix: Merge user-message-hook output into context-hook hookSpecificOutput

- Add footer message to additionalContext in context-hook.ts
- Remove user-message-hook from SessionStart hooks array
- Fixes issue where stderr+exit(1) approach was silently discarded

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update logs and documentation for recent plugin and worker service changes

- Added detailed logs for worker service activities from Dec 10, 2025 to Jan 7, 2026, including initialization patterns, cleanup confirmations, and diagnostic logging.
- Updated plugin documentation with recent activities, including plugin synchronization and configuration changes from Dec 3, 2025 to Jan 7, 2026.
- Enhanced the context hook and worker service logs to reflect improvements and fixes in the plugin architecture.
- Documented the migration and verification processes for the Claude memory system and its integration with the marketplace.

* Refactor hooks architecture and remove deprecated user-message-hook

- Updated hook configurations in CLAUDE.md and hooks.json to reflect changes in session start behavior.
- Removed user-message-hook functionality as it is no longer utilized in Claude Code 2.1.0; context is now injected silently.
- Enhanced context-hook to handle session context injection without user-visible messages.
- Cleaned up documentation across multiple files to align with the new hook structure and removed references to obsolete hooks.
- Adjusted timing and command execution for hooks to improve performance and reliability.

* fix: Address PR #610 review issues

- Replace USER_MESSAGE_ONLY test with BLOCKING_ERROR test in hook-constants.test.ts
- Standardize Claude Code 2.1.0 note wording across all three documentation files
- Exclude deprecated user-message-hook.ts from logger-usage-standards test

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: Remove hardcoded fake token counts from context injection

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR #610 review issues by fixing test files, standardizing documentation notes, and verifying code quality improvements.

* fix: Add path validation to CLAUDE.md distribution to prevent invalid directory creation

- Add isValidPathForClaudeMd() function to reject invalid paths:
  - Tilde paths (~) that Node.js doesn't expand
  - URLs (http://, https://)
  - Paths with spaces (likely command text or PR references)
  - Paths with # (GitHub issue/PR references)
  - Relative paths that escape project boundary

- Integrate validation in updateFolderClaudeMdFiles loop
- Add 6 unit tests for path validation
- Update .gitignore to prevent accidental commit of malformed directories
- Clean up existing invalid directories (~/, PR #610..., git diff..., https:)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Implement path validation in CLAUDE.md generation to prevent invalid directory creation

- Added `isValidPathForClaudeMd()` function to validate file paths in `src/utils/claude-md-utils.ts`.
- Integrated path validation in `updateFolderClaudeMdFiles` to skip invalid paths.
- Added 6 new unit tests in `tests/utils/claude-md-utils.test.ts` to cover various rejection cases.
- Updated `.gitignore` to prevent tracking of invalid directories.
- Cleaned up existing invalid directories in the repository.

* feat: Promote critical WARN logs to ERROR level across codebase

Comprehensive log-level audit promoting 38+ WARN messages to ERROR for
improved debugging and incident response:

- Parser: observation type errors, data contamination
- SDK/Agents: empty init responses (Gemini, OpenRouter)
- Worker/Queue: session recovery, auto-recovery failures
- Chroma: sync failures, search failures (now treated as critical)
- SQLite: search failures (primary data store)
- Session/Generator: failures, missing context
- Infrastructure: shutdown, process management failures
- File Operations: CLAUDE.md updates, config reads
- Branch Management: recovery checkout failures

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Address PR #614 review issues

- Remove incorrectly tracked tilde-prefixed files from git
- Fix absolute path validation to check projectRoot boundaries
- Add test coverage for absolute path validation edge cases

Closes review issues:
- Issue 1: ~/ prefixed files removed from tracking
- Issue 3: Absolute paths now validated against projectRoot
- Issue 4: Added 3 new test cases for absolute path scenarios

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* build assets and context

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:34:20 -05:00

412 lines
12 KiB
Markdown

# Issue #590: Blank Terminal Window Pops Up on Windows When Chroma MCP Server Starts
**Date:** 2026-01-07
**Issue Author:** dwd898
**Severity:** Medium (UX disruption, not a functional failure)
**Status:** OPEN - Root cause confirmed, multiple solutions proposed
---
## 1. Executive Summary
On Windows 11, when claude-mem starts the Chroma MCP server via `uvx`, a blank terminal window (Windows Terminal / PowerShell) appears and does not close automatically. Users must manually close this window each time, which disrupts the workflow.
The root cause is that the MCP SDK's `StdioClientTransport` class does not pass the `windowsHide: true` option to the underlying `child_process.spawn()` call. While the claude-mem codebase attempts to set this option, it has no effect because the MCP SDK ignores it.
This issue affects all Windows users who have ChromaDB vector search enabled (the default configuration).
---
## 2. Problem Analysis
### 2.1 User-Reported Symptoms
- A blank terminal window appears when any action triggers Chroma initialization
- The window shows the `uvx.exe` path but contains no output
- The window remains open until manually closed by the user
- This occurs every time ChromaDB is initialized (typically once per Claude session)
### 2.2 Environment Details
| Component | Value |
|-----------|-------|
| OS | Windows 11 64-bit |
| Terminal | PowerShell 7.6.0-preview.6 |
| claude-mem version | 9.0.0 |
| uvx location | `C:\Users\Dell\AppData\Local\Microsoft\WinGet\Links\uvx.exe` |
| MCP SDK version | ^1.25.1 |
### 2.3 Trigger Conditions
The terminal popup occurs when:
1. Claude Code starts a new session with claude-mem enabled
2. A search query is executed with semantic search enabled
3. The ChromaSync service initializes for the first time in a session
4. Any backfill operation triggers Chroma connection
---
## 3. Technical Details
### 3.1 Affected Code Location
**File:** `/Users/alexnewman/conductor/workspaces/claude-mem/budapest/src/services/sync/ChromaSync.ts`
**Lines:** 106-124
```typescript
const transportOptions: any = {
command: 'uvx',
args: [
'--python', pythonVersion,
'chroma-mcp',
'--client-type', 'persistent',
'--data-dir', this.VECTOR_DB_DIR
],
stderr: 'ignore'
};
// CRITICAL: On Windows, try to hide console window to prevent PowerShell popups
// Note: windowsHide may not be supported by MCP SDK's StdioClientTransport
if (isWindows) {
transportOptions.windowsHide = true;
logger.debug('CHROMA_SYNC', 'Windows detected, attempting to hide console window', { project: this.project });
}
this.transport = new StdioClientTransport(transportOptions);
```
### 3.2 Why windowsHide Fails
The `StdioClientTransport` class from `@modelcontextprotocol/sdk` accepts configuration options but does **not** forward `windowsHide` to `child_process.spawn()`. The SDK's transport implementation only uses a subset of spawn options:
- `command` - The executable to run
- `args` - Command line arguments
- `env` - Environment variables (optional)
- `stderr` - Stderr handling mode
The `windowsHide` option is silently ignored because it's not part of the SDK's expected interface.
### 3.3 MCP SDK Transport Architecture
```
ChromaSync.ts
|
v
StdioClientTransport (MCP SDK)
|
v
child_process.spawn() [internal to SDK]
|
v
uvx.exe subprocess
|
v
chroma-mcp Python process
```
The SDK controls the spawn call, so claude-mem cannot directly influence the spawn options.
### 3.4 Comparison with Other Subprocess Calls
Other parts of claude-mem successfully hide Windows console windows because they use `child_process.spawn()` directly:
| Component | File | Uses windowsHide | Works on Windows |
|-----------|------|------------------|------------------|
| ProcessManager | `ProcessManager.ts:271` | Yes (direct spawn) | Yes |
| SDKAgent | `SDKAgent.ts:379` | Yes (direct spawn) | Yes |
| BranchManager | `BranchManager.ts:61,88` | Yes (direct spawn) | Yes |
| shared/paths | `paths.ts:103` | Yes (direct spawn) | Yes |
| ChromaSync | `ChromaSync.ts:120` | Yes (via SDK - ignored) | **No** |
---
## 4. Impact Assessment
### 4.1 Affected Users
- All Windows users with ChromaDB enabled (default)
- Approximately 100% of Windows user base
### 4.2 Severity Breakdown
| Aspect | Impact |
|--------|--------|
| Functionality | No impact - Chroma works correctly |
| UX Disruption | Medium - Requires manual window close |
| Workflow Impact | Low - One-time per session |
| Data Integrity | None |
| Security | None |
### 4.3 Workaround Availability
**Current Workaround:** Users can manually close the terminal window. The Chroma process continues running in the background even after the window is closed.
---
## 5. Root Cause Analysis
### 5.1 Primary Cause
The MCP SDK's `StdioClientTransport` class does not implement support for the `windowsHide` spawn option. This is a limitation in the SDK, not a bug in claude-mem.
### 5.2 SDK Gap Analysis
The MCP SDK (version 1.25.1) provides a transport abstraction layer but does not expose all Node.js spawn options. The `StdioClientTransport` constructor signature accepts:
```typescript
interface StdioClientTransportOptions {
command: string;
args?: string[];
env?: NodeJS.ProcessEnv;
stderr?: 'inherit' | 'pipe' | 'ignore';
}
```
Notable missing options:
- `windowsHide`
- `detached`
- `cwd`
- `shell`
### 5.3 Historical Context
The claude-mem codebase has extensively addressed Windows console popup issues in other areas:
- **December 4, 2025:** Added `windowsHide` parameter to ProcessManager
- **December 17, 2025:** PR #378 standardized `windowsHide: true` across all direct spawn calls
- **Known Issue:** The comment in ChromaSync.ts (line 118) explicitly acknowledges this limitation
---
## 6. Recommended Solutions
### 6.1 Solution 1: PowerShell Wrapper (Recommended Short-Term)
**Approach:** Wrap the `uvx` command in a PowerShell invocation that hides the window.
**Implementation:**
```typescript
const transportOptions: any = {
command: 'powershell',
args: [
'-WindowStyle', 'Hidden',
'-Command',
`uvx --python ${pythonVersion} chroma-mcp --client-type persistent --data-dir '${this.VECTOR_DB_DIR}'`
],
stderr: 'ignore'
};
```
**Pros:**
- No SDK changes required
- Immediate fix possible
- Pattern already used in worker-cli.js (lines 1-19)
**Cons:**
- Adds PowerShell dependency (already required for Windows)
- Slightly more complex command construction
- PATH escaping considerations
**Estimated Effort:** 2-4 hours
### 6.2 Solution 2: Custom Transport Layer
**Approach:** Bypass `StdioClientTransport` and implement a custom transport using `child_process.spawn()` directly.
**Implementation:**
```typescript
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
import { spawn, ChildProcess } from 'child_process';
class WindowsHiddenStdioTransport implements Transport {
private process: ChildProcess;
constructor(options: TransportOptions) {
this.process = spawn(options.command, options.args, {
windowsHide: true,
stdio: ['pipe', 'pipe', options.stderr === 'ignore' ? 'ignore' : 'pipe']
});
}
// ... implement Transport interface
}
```
**Pros:**
- Full control over spawn options
- Clean, maintainable solution
- Reusable for other MCP clients
**Cons:**
- Requires implementing Transport interface
- Must handle stdin/stdout piping manually
- More complex error handling
**Estimated Effort:** 8-16 hours
### 6.3 Solution 3: Upstream SDK Enhancement
**Approach:** Request the MCP SDK team to add `windowsHide` support to `StdioClientTransport`.
**Implementation:**
1. Open issue on MCP SDK repository
2. Propose API extension: `spawnOptions?: Partial<SpawnOptions>`
3. Provide PR if accepted
**Pros:**
- Fixes the root cause
- Benefits all MCP SDK users on Windows
- No workarounds needed
**Cons:**
- Depends on external team
- Uncertain timeline
- May require SDK version bump
**Estimated Effort:** Variable (depends on upstream response)
### 6.4 Solution 4: VBS Wrapper Script
**Approach:** Use a Windows Script Host (VBS) file to launch the process silently.
**Implementation:**
Create `launch-chroma.vbs`:
```vbs
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run "uvx --python 3.13 chroma-mcp --client-type persistent --data-dir " & DataDir, 0, False
```
**Pros:**
- Guaranteed hidden window
- Works on all Windows versions
**Cons:**
- Requires additional script file
- Complex path handling
- VBS is deprecated technology
**Estimated Effort:** 4-6 hours
---
## 7. Priority/Severity Assessment
### 7.1 Severity Matrix
| Factor | Rating | Justification |
|--------|--------|---------------|
| User Impact | Medium | Annoying but not blocking |
| Frequency | Low | Once per session |
| Workaround | Yes | Close window manually |
| Data Risk | None | No data loss or corruption |
| Security Risk | None | No security implications |
### 7.2 Recommended Priority
**Priority: P2 (Medium)**
This issue should be addressed in the next minor release but is not urgent enough to warrant an immediate patch release.
### 7.3 Recommendation
Implement **Solution 1 (PowerShell Wrapper)** as an immediate fix for the next release. Simultaneously, open an upstream issue for **Solution 3** to address the root cause in the MCP SDK.
---
## 8. Related Issues and Context
### 8.1 Related GitHub Issues
| Issue | Title | Relationship |
|-------|-------|--------------|
| #367 | Console windows appearing during hook execution | Similar root cause |
| #517 | PowerShell `$_` escaping in Git Bash | Windows shell escaping |
| #555 | Windows hooks IPC issues | Windows platform challenges |
### 8.2 Related PRs
| PR | Title | Relevance |
|----|-------|-----------|
| #378 | Windows stabilization | Added windowsHide to other spawn calls |
| #372 | Worker wrapper architecture | Similar Windows console hiding approach |
### 8.3 Documentation
- Windows Woes Report: `/docs/reports/2026-01-06--windows-woes-comprehensive-report.md`
- Windows Troubleshooting: https://docs.claude-mem.ai/troubleshooting/windows-issues
---
## 9. Testing Recommendations
### 9.1 Test Cases
1. **Basic functionality:** Verify Chroma starts correctly with proposed fix
2. **Window visibility:** Confirm no terminal window appears
3. **Process lifecycle:** Ensure Chroma process terminates on worker shutdown
4. **Error handling:** Verify errors are properly captured despite hidden window
5. **PATH variations:** Test with uvx in different PATH locations
### 9.2 Test Environments
- Windows 11 with PowerShell 7.x
- Windows 11 with PowerShell 5.1
- Windows 10 with PowerShell 5.1
- Windows with Git Bash as default shell
---
## 10. Appendix
### 10.1 Current ChromaSync Connection Flow
```
1. ChromaSync.ensureConnection() called
2. Check if already connected
3. Load Python version from settings
4. Detect Windows platform
5. Set windowsHide: true (ineffective)
6. Create StdioClientTransport with uvx command
7. Connect MCP client to transport
-> POPUP APPEARS HERE
8. Mark as connected
```
### 10.2 PowerShell Command Pattern (from worker-cli.js)
The existing pattern for hidden PowerShell execution:
```typescript
const cmd = `Start-Process -FilePath '${escapedPath}' -ArgumentList '${args}' -WindowStyle Hidden`;
spawnSync("powershell", ["-Command", cmd], {
stdio: "pipe",
timeout: 10000,
windowsHide: true
});
```
### 10.3 MCP SDK Source Reference
The StdioClientTransport implementation in `@modelcontextprotocol/sdk` uses:
```typescript
this._process = spawn(command, args, {
env: this._env,
stdio: ['pipe', 'pipe', stderr]
});
```
Note the absence of `windowsHide` in the spawn options.
---
## 11. Revision History
| Date | Author | Changes |
|------|--------|---------|
| 2026-01-07 | Claude Opus 4.5 | Initial report |