diff --git a/CLAUDE-PLATFORM.md b/CLAUDE-PLATFORM.md new file mode 100644 index 00000000..69a9125b --- /dev/null +++ b/CLAUDE-PLATFORM.md @@ -0,0 +1,221 @@ +# 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:** +```typescript +// 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:** +```typescript +// Windows uses ';', Unix uses ':' +const delimiter = path.delimiter; // Use this, don't hardcode +``` + +**Tilde expansion:** +```typescript +// Node.js fs does NOT expand ~ automatically +import { expandTilde } from '../shared/pathUtils'; +const expanded = expandTilde('~/.config/file'); // Always use this +``` + +**Minimum path lengths:** +```typescript +// Validation must account for platform differences +const minPathLength = process.platform === 'win32' ? 4 : 5; // C:\a vs /a/b +``` + +**Windows reserved names:** +```typescript +// 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:** +```typescript +// Windows: $SHELL doesn't exist; default to PowerShell +const defaultShell = process.platform === 'win32' ? 'powershell.exe' : 'bash'; +``` + +**Command lookup differs:** +```typescript +// 'which' on Unix, 'where' on Windows +const command = process.platform === 'win32' ? 'where' : 'which'; +``` + +**Executable permissions:** +```typescript +// Unix requires X_OK check; Windows does not +if (process.platform !== 'win32') { + await fs.promises.access(filePath, fs.constants.X_OK); +} +``` + +**Windows shell execution:** +```typescript +// Some Windows commands need shell: true +const useShell = isWindows && needsWindowsShell(command); +``` + +### 3. SSH Remote Execution + +**Two SSH identifiers with different lifecycles:** +```typescript +// 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:** +```typescript +// chokidar can't watch remote directories +if (sshRemoteId) { + // Use polling instead of file watching +} +``` + +**Prompts must go via stdin for SSH:** +```typescript +// 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:** +```typescript +// 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:** +```typescript +// Claude Code: session_id +// Codex: thread_id +// Different field names, same concept +``` + +**Storage locations differ per platform:** +```typescript +// Claude Code: ~/.claude/projects// +// Codex: ~/.codex/sessions/YYYY/MM/DD/*.jsonl +// OpenCode: +// - macOS/Linux: ~/.config/opencode/storage/ +// - Windows: %APPDATA%/opencode/storage/ +``` + +**Resume flags differ:** +```typescript +// Claude Code: --resume +// Codex: resume (subcommand, not flag) +// OpenCode: --session +``` + +**Read-only mode flags differ:** +```typescript +// Claude Code: --permission-mode plan +// Codex: --sandbox read-only +// OpenCode: --agent plan +``` + +### 5. Keyboard & Input + +**macOS Alt key produces special characters:** +```typescript +// 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:** +```typescript +// cmd.exe has ~8KB command line limit +// Use sendPromptViaStdin to bypass this for long prompts +``` + +### 6. Git Operations + +**Git stat format differs:** +```typescript +// GNU stat vs BSD stat have different format specifiers +// Use git commands that work cross-platform +``` + +**Case sensitivity:** +```typescript +// 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` | diff --git a/CLAUDE-WIZARD.md b/CLAUDE-WIZARD.md index af49e083..709f8286 100644 --- a/CLAUDE-WIZARD.md +++ b/CLAUDE-WIZARD.md @@ -87,16 +87,16 @@ The Wizard maintains two types of state: - Cleared on completion or when user chooses "Just Quit" **State Save Triggers:** -- Auto-save: When `currentStep` changes (step > 1) - `WizardContext.tsx:791` -- Manual save: User clicks "Save & Exit" - `MaestroWizard.tsx:147` +- Auto-save: When `currentStep` changes (step > 1) - `WizardContext.tsx` useEffect with `saveResumeState()` +- Manual save: User clicks "Save & Exit" - `MaestroWizard.tsx` `handleConfirmExit()` **State Clear Triggers:** -- Wizard completion: `App.tsx:4681` + `WizardContext.tsx:711` -- User quits: "Just Quit" button - `MaestroWizard.tsx:168` +- Wizard completion: `App.tsx` wizard completion handler + `WizardContext.tsx` `COMPLETE_WIZARD` action +- User quits: "Quit without saving" button - `MaestroWizard.tsx` `handleQuitWithoutSaving()` - User starts fresh: "Start Fresh" in resume modal - `App.tsx` resume handlers **Opening Wizard Logic:** -The `openWizard()` function in `WizardContext.tsx:528-535` handles state initialization: +The `openWizard()` function in `WizardContext.tsx` handles state initialization: ```typescript // If previous wizard was completed, reset in-memory state first if (state.isComplete === true) { diff --git a/CLAUDE.md b/CLAUDE.md index e6676bef..893c28f6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,10 +15,35 @@ This guide has been split into focused sub-documents for progressive disclosure: | [[CLAUDE-FEATURES.md]] | Usage Dashboard and Document Graph features | | [[CLAUDE-AGENTS.md]] | Supported agents and capabilities | | [[CLAUDE-SESSION.md]] | Session interface and code conventions | +| [[CLAUDE-PLATFORM.md]] | Cross-platform concerns (Windows, Linux, macOS, SSH remote) | | [AGENT_SUPPORT.md](AGENT_SUPPORT.md) | Detailed agent integration guide | --- +## Agent Behavioral Guidelines + +Core behaviors for effective collaboration. Failures here cause the most rework. + +### Surface Assumptions Early +Before implementing non-trivial work, explicitly state assumptions. Never silently fill in ambiguous requirements—the most common failure mode is guessing wrong and running with it. Format: "Assumptions: 1) X, 2) Y. Correct me now or I proceed." + +### Manage Confusion Actively +When encountering inconsistencies, conflicting requirements, or unclear specs: **STOP**. Name the specific confusion, present the tradeoff, and wait for resolution. Bad: silently picking one interpretation. Good: "I see X in file A but Y in file B—which takes precedence?" + +### Push Back When Warranted +Not a yes-machine. When an approach has clear problems: point out the issue directly, explain the concrete downside, propose an alternative, then accept the decision if overridden. Sycophancy ("Of course!") followed by implementing a bad idea helps no one. + +### Enforce Simplicity +Natural tendency is to overcomplicate—actively resist. Before finishing: Can this be fewer lines? Are abstractions earning their complexity? Would a senior dev say "why didn't you just..."? Prefer the boring, obvious solution. + +### Maintain Scope Discipline +Touch only what's asked. Do NOT: remove comments you don't understand, "clean up" orthogonal code, refactor adjacent systems as side effects, or delete seemingly-unused code without approval. Surgical precision, not unsolicited renovation. + +### Dead Code Hygiene +After refactoring: identify now-unreachable code, list it explicitly, ask "Should I remove these now-unused elements: [list]?" Don't leave corpses. Don't delete without asking. + +--- + ## Standardized Vernacular Use these terms consistently in code, comments, and documentation: @@ -50,6 +75,7 @@ Maestro is an Electron desktop app for managing multiple AI coding assistants si | `claude-code` | Claude Code | **Active** | | `codex` | OpenAI Codex | **Active** | | `opencode` | OpenCode | **Active** | +| `factory-droid` | Factory Droid | **Active** | | `terminal` | Terminal | Internal | See [[CLAUDE-AGENTS.md]] for capabilities and integration details. @@ -79,22 +105,13 @@ npm run test:watch # Run tests in watch mode src/ ├── main/ # Electron main process (Node.js) │ ├── index.ts # Entry point, IPC handlers -│ ├── process-manager.ts # Process spawning (PTY + child_process) │ ├── preload.ts # Secure IPC bridge -│ ├── agent-detector.ts # Agent detection and configuration -│ ├── agent-capabilities.ts # Agent capability definitions -│ ├── agent-session-storage.ts # Session storage interface -│ ├── parsers/ # Agent output parsers -│ │ ├── agent-output-parser.ts # Parser interface -│ │ ├── claude-output-parser.ts # Claude Code parser -│ │ ├── opencode-output-parser.ts # OpenCode parser -│ │ └── error-patterns.ts # Error detection patterns -│ ├── storage/ # Session storage implementations -│ │ ├── claude-session-storage.ts -│ │ └── opencode-session-storage.ts -│ ├── tunnel-manager.ts # Cloudflare tunnel support -│ ├── web-server.ts # Fastify server for web/mobile interface -│ └── utils/execFile.ts # Safe command execution +│ ├── process-manager.ts # Process spawning (PTY + child_process) +│ ├── agent-*.ts # Agent detection, capabilities, session storage +│ ├── parsers/ # Per-agent output parsers + error patterns +│ ├── storage/ # Per-agent session storage implementations +│ ├── ipc/handlers/ # IPC handler modules (stats, git, playbooks, etc.) +│ └── utils/ # Utilities (execFile, ssh-spawn-wrapper, etc.) │ ├── renderer/ # React frontend (desktop) │ ├── App.tsx # Main coordinator @@ -102,31 +119,21 @@ src/ │ ├── hooks/ # Custom React hooks │ ├── services/ # IPC wrappers (git.ts, process.ts) │ ├── constants/ # Themes, shortcuts, priorities -│ └── contexts/ # Layer stack context +│ └── contexts/ # Context providers (LayerStack, etc.) │ ├── web/ # Web/mobile interface │ ├── mobile/ # Mobile-optimized React app -│ ├── components/ # Shared web components -│ └── hooks/ # Web-specific hooks +│ └── components/ # Shared web components │ ├── cli/ # CLI tooling for batch automation │ ├── commands/ # CLI command implementations -│ ├── services/ # Playbook and batch processing -│ └── index.ts # CLI entry point +│ └── services/ # Playbook and batch processing │ ├── prompts/ # System prompts (editable .md files) -│ ├── wizard-*.md # Wizard conversation prompts -│ ├── autorun-*.md # Auto Run default prompts -│ └── index.ts # Central exports │ ├── shared/ # Shared types and utilities -│ ├── types.ts # Common type definitions -│ └── templateVariables.ts # Template variable processing │ └── docs/ # Mintlify documentation (docs.runmaestro.ai) - ├── docs.json # Navigation and configuration - ├── screenshots/ # All documentation screenshots - └── *.md # Documentation pages ``` --- @@ -174,6 +181,52 @@ src/ ## Critical Implementation Guidelines +### Error Handling & Sentry + +Maestro uses Sentry for error tracking. Field data from production crashes is invaluable for improving code quality. + +**DO let exceptions bubble up:** +```typescript +// WRONG - silently swallowing errors hides bugs from Sentry +try { + await riskyOperation(); +} catch (e) { + console.error(e); // Lost to the void +} + +// CORRECT - let unhandled exceptions reach Sentry +await riskyOperation(); // Crashes are reported automatically +``` + +**DO handle expected/recoverable errors explicitly:** +```typescript +// CORRECT - known failure modes should be handled gracefully +try { + await fetchUserData(); +} catch (e) { + if (e.code === 'NETWORK_ERROR') { + showOfflineMessage(); // Expected, recoverable + } else { + throw e; // Unexpected - let Sentry capture it + } +} +``` + +**DO use Sentry utilities for explicit reporting:** +```typescript +import { captureException, captureMessage } from '../utils/sentry'; + +// Report exceptions with context +await captureException(error, { userId, operation: 'sync' }); + +// Report notable events that aren't crashes +await captureMessage('Unusual state detected', 'warning', { state }); +``` + +**Key files:** `src/main/utils/sentry.ts`, `src/renderer/components/ErrorBoundary.tsx` + +--- + ### SSH Remote Execution Awareness **IMPORTANT:** When implementing any feature that spawns agent processes (e.g., context grooming, group chat, batch operations), you MUST support SSH remote execution.