mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 00:21:21 +00:00
## 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 🗺️
This commit is contained in:
221
CLAUDE-PLATFORM.md
Normal file
221
CLAUDE-PLATFORM.md
Normal file
@@ -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/<encoded-path>/
|
||||
// Codex: ~/.codex/sessions/YYYY/MM/DD/*.jsonl
|
||||
// OpenCode:
|
||||
// - macOS/Linux: ~/.config/opencode/storage/
|
||||
// - Windows: %APPDATA%/opencode/storage/
|
||||
```
|
||||
|
||||
**Resume flags differ:**
|
||||
```typescript
|
||||
// Claude Code: --resume <session-id>
|
||||
// Codex: resume <thread_id> (subcommand, not flag)
|
||||
// OpenCode: --session <session-id>
|
||||
```
|
||||
|
||||
**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` |
|
||||
@@ -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) {
|
||||
|
||||
109
CLAUDE.md
109
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.
|
||||
|
||||
Reference in New Issue
Block a user