Files
Maestro/CLAUDE-PLATFORM.md
Pedram Amini bcf6c4e60d ## CHANGES
- Added brand-new cross-platform guide covering Windows, Linux, macOS, SSH gotchas 🌍
- Documented SSH remote constraints: no chokidar watch, stdin prompts, path norms 🛰️
- Standardized path best-practices: `path.join`, `path.posix`, delimiters, tilde expansion 🧭
- Clarified shell execution differences: default shells, which/where lookup, permissions 🐚
- Codified agent portability quirks: session IDs, storage locations, resume flags 📦
- Introduced keyboard/input pitfalls: macOS Alt keycodes, Windows command-length limits ⌨️
- Added Git cross-platform warnings: stat differences and case-sensitivity traps 🧩
- Upgraded collaboration rules: assumptions, confusion stops, pushback, scope discipline 🤝
- Added Sentry/error-handling guidance: bubble unexpected errors, report with context 🔎
- Refreshed docs structure references to match current `src/` layout and modules 🗺️
2026-02-03 19:31:52 -06:00

6.1 KiB

CLAUDE-PLATFORM.md

Cross-platform and multi-environment considerations for the Maestro codebase. For the main guide, see CLAUDE.md.


Platform Compatibility Matrix

Feature macOS Windows Linux SSH Remote
Claude Code Full Full Full Full
OpenAI Codex Full Full Full Full
OpenCode Full Full Full Full
Factory Droid Full Full Full Full
File watching (chokidar) Yes Yes Yes No
Git worktrees Yes Yes Yes Yes
PTY terminal Yes Yes Yes N/A

Critical Platform Gotchas

1. Path Handling

Path separators differ:

// WRONG - hardcoded separator
const fullPath = folder + '/' + filename;

// CORRECT - use path.join or path.posix for SSH
import * as path from 'path';
const fullPath = path.join(folder, filename);           // Local
const remotePath = path.posix.join(folder, filename);   // SSH remote

Path delimiters differ:

// Windows uses ';', Unix uses ':'
const delimiter = path.delimiter;  // Use this, don't hardcode

Tilde expansion:

// Node.js fs does NOT expand ~ automatically
import { expandTilde } from '../shared/pathUtils';
const expanded = expandTilde('~/.config/file');  // Always use this

Minimum path lengths:

// Validation must account for platform differences
const minPathLength = process.platform === 'win32' ? 4 : 5;  // C:\a vs /a/b

Windows reserved names:

// CON, PRN, AUX, NUL, COM1-9, LPT1-9 are invalid on Windows
const reservedNames = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;

2. Shell Detection & Execution

Default shells differ:

// Windows: $SHELL doesn't exist; default to PowerShell
const defaultShell = process.platform === 'win32' ? 'powershell.exe' : 'bash';

Command lookup differs:

// 'which' on Unix, 'where' on Windows
const command = process.platform === 'win32' ? 'where' : 'which';

Executable permissions:

// Unix requires X_OK check; Windows does not
if (process.platform !== 'win32') {
    await fs.promises.access(filePath, fs.constants.X_OK);
}

Windows shell execution:

// Some Windows commands need shell: true
const useShell = isWindows && needsWindowsShell(command);

3. SSH Remote Execution

Two SSH identifiers with different lifecycles:

// sshRemoteId: Set AFTER AI agent spawns (via onSshRemote callback)
// sessionSshRemoteConfig.remoteId: Set BEFORE spawn (user configuration)

// WRONG - fails for terminal-only SSH sessions
const sshId = session.sshRemoteId;

// CORRECT - works for all SSH sessions
const sshId = session.sshRemoteId || session.sessionSshRemoteConfig?.remoteId;

File watching not available for SSH:

// chokidar can't watch remote directories
if (sshRemoteId) {
    // Use polling instead of file watching
}

Prompts must go via stdin for SSH:

// IMPORTANT: ALL agent prompts are passed via stdin passthrough for SSH.
// This avoids shell escaping issues and command line length limits.
if (isSSH) {
    // Pass prompt via stdin, not as command line argument
}

Path resolution on remote:

// Don't resolve paths locally when operating on remote
// The remote may have different filesystem structure
if (isRemote) {
    // Use path as-is, normalize slashes only
}

4. Agent-Specific Differences

Session ID terminology:

// Claude Code: session_id
// Codex: thread_id
// Different field names, same concept

Storage locations differ per platform:

// Claude Code: ~/.claude/projects/<encoded-path>/
// Codex: ~/.codex/sessions/YYYY/MM/DD/*.jsonl
// OpenCode:
//   - macOS/Linux: ~/.config/opencode/storage/
//   - Windows: %APPDATA%/opencode/storage/

Resume flags differ:

// Claude Code: --resume <session-id>
// Codex: resume <thread_id> (subcommand, not flag)
// OpenCode: --session <session-id>

Read-only mode flags differ:

// Claude Code: --permission-mode plan
// Codex: --sandbox read-only
// OpenCode: --agent plan

5. Keyboard & Input

macOS Alt key produces special characters:

// Alt+L = '¬', Alt+P = 'π', Alt+U = 'ü'
// Must use e.code for Alt key combos, not e.key
if (e.altKey && process.platform === 'darwin') {
    const key = e.code.replace('Key', '').toLowerCase();  // 'KeyL' -> 'l'
}

Windows command line length limit:

// cmd.exe has ~8KB command line limit
// Use sendPromptViaStdin to bypass this for long prompts

6. Git Operations

Git stat format differs:

// GNU stat vs BSD stat have different format specifiers
// Use git commands that work cross-platform

Case sensitivity:

// macOS with case-insensitive filesystem:
// Renaming "readme.md" to "README.md" may not trigger expected events

Testing Checklist

When making changes that involve any of the above areas, verify:

  • Works on macOS (primary development platform)
  • Works on Windows (PowerShell default, path separators)
  • Works on Linux (standard Unix behavior)
  • Works with SSH remote sessions (no file watching, stdin passthrough)
  • Path handling uses path.join or path.posix as appropriate
  • No hardcoded path separators (/ or \)
  • Shell commands use platform-appropriate lookup (which/where)
  • Agent-specific code handles all supported agents, not just Claude

Key Files for Platform Logic

Concern Primary Files
Path utilities src/shared/pathUtils.ts
Shell detection src/main/utils/shellDetector.ts
WSL detection src/main/utils/wslDetector.ts
CLI detection src/main/utils/cliDetection.ts
SSH spawn wrapper src/main/utils/ssh-spawn-wrapper.ts
SSH command builder src/main/utils/ssh-command-builder.ts
Agent path probing src/main/agents/path-prober.ts
Windows diagnostics src/main/debug-package/collectors/windows-diagnostics.ts
Safe exec src/main/utils/execFile.ts