Skip to main content

codexmonitor-orchestration

Expert in CodexMonitor, a Tauri app for orchestrating multiple Codex agents across local workspaces with threads, git integration, and remote daemon support.

Quellinformationen

Repository
reason-machines/codex-skills
Letzte Quellaktivität
16. Mai 2026 um 19:30
Erkannte Sprache von SKILL.md
Englisch
Sterne
0
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
codexmonitor-orchestration
description
Expert in CodexMonitor, a Tauri app for orchestrating multiple Codex agents across local workspaces with threads, git integration, and remote daemon support.
triggers
["how do I use CodexMonitor","set up CodexMonitor workspaces","manage Codex agent threads","CodexMonitor remote backend","build CodexMonitor from source","configure CodexMonitor iOS","CodexMonitor git integration","run CodexMonitor daemon"]
# CodexMonitor Orchestration Skill > Skill by [ara.so](https://ara.so) — Codex Skills collection. CodexMonitor is a Tauri-based desktop and mobile app for orchestrating multiple Codex agents across local workspaces. It provides workspace management, thread persistence, git/GitHub integration, file browsing, prompt libraries, and a remote daemon mode for connecting iOS clients or headless setups. ## What CodexMonitor Does - **Multi-workspace orchestration**: Spawn one `codex app-server` per workspace, resume threads, track unread/running state - **Thread management**: Pin, rename, archive, copy threads; per-thread drafts; stop/interrupt in-flight turns - **Worktree agents**: Clone agents for isolated work under app data directory (legacy `.codex-worktrees` supported) - **Git & GitHub**: Diff stats, staged/unstaged files, commit log, branch management, GitHub Issues/PRs via `gh` - **Composer**: Image attachments, autocomplete for skills (`$`), prompts (`/prompts:`), reviews (`/review`), file paths (`@`) - **Remote daemon**: Run Codex on another machine, connect iOS client via TCP (Tailscale support) - **Prompt library**: Global/workspace prompts with create/edit/delete/move and run in threads - **File tree**: Search, file-type icons, reveal in Finder/Explorer - **Terminal dock**: Multiple tabs for background commands (experimental) ## Installation ### Requirements - Node.js + npm - Rust toolchain (stable) - CMake (for native dependencies, dictation/Whisper) - LLVM/Clang (Windows only, for bindgen) - Codex CLI installed and in `PATH` - Git CLI (for worktree operations) - GitHub CLI `gh` (optional, for GitHub integrations) ### Install Dependencies ```bash npm install ``` ### Check Environment ```bash npm run doctor ``` ### Run in Development ```bash npm run tauri:dev ``` ### Build Production Bundle ```bash # macOS/Linux npm run tauri:build # Windows (opt-in, uses separate config) npm run tauri:build:win ``` Artifacts: `src-tauri/target/release/bundle/` (platform-specific subfolders) ## Workspace Management ### Adding a Workspace Workspaces persist to `workspaces.json` in app data directory. **Via UI**: Sidebar → Add workspace → Select directory **Data structure** (`src-tauri/src/workspaces/mod.rs`): ```rust #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Workspace { pub id: String, pub name: String, pub path: String, pub codex_home: Option<String>, // Overrides global Codex home pub is_remote: bool, pub remote_host: Option<String>, pub remote_token: Option<String>, } ``` ### Programmatic Workspace Access Frontend service (`src/services/tauri.ts`): ```typescript import { invoke } from '@tauri-apps/api/tauri'; // Load all workspaces const workspaces = await invoke<Workspace[]>('workspace_list'); // Add workspace const newWorkspace = await invoke<Workspace>('workspace_add', { path: '/path/to/project', name: 'My Project', }); // Update workspace await invoke('workspace_update', { id: 'workspace-id', updates: { codex_home: '/custom/codex/home' }, }); // Remove workspace await invoke('workspace_remove', { id: 'workspace-id' }); ``` Backend command (`src-tauri/src/lib.rs`): ```rust #[tauri::command] async fn workspace_list( state: tauri::State<'_, AppState>, ) -> Result<Vec<Workspace>, String> { state.workspace_manager.lock().await.list_workspaces() .map_err(|e| e.to_string()) } ``` ## Thread Management ### Thread Reducer Architecture Thread state is managed by a reducer with slices in `src/features/threads/hooks/threadReducer/`. **Thread reducer pattern** (`src/features/threads/hooks/threadReducer/index.ts`): ```typescript export type ThreadAction = | { type: 'SET_MESSAGES'; messages: Message[] } | { type: 'ADD_MESSAGE'; message: Message } | { type: 'UPDATE_MESSAGE'; messageId: string; updates: Partial<Message> } | { type: 'SET_RUNNING'; running: boolean } | { type: 'SET_UNREAD'; unread: number } | { type: 'RESET' }; export function threadReducer(state: ThreadState, action: ThreadAction): ThreadState { switch (action.type) { case 'SET_MESSAGES': return { ...state, messages: action.messages }; case 'ADD_MESSAGE': return { ...state, messages: [...state.messages, action.message] }; case 'UPDATE_MESSAGE': return { ...state, messages: state.messages.map(m => m.id === action.messageId ? { ...m, ...action.updates } : m ), }; case 'SET_RUNNING': return { ...state, running: action.running }; case 'RESET': return initialThreadState; default: return state; } } ``` ### Resuming a Thread Frontend (`src/features/threads/hooks/useThreadResume.ts`): ```typescript import { invoke } from '@tauri-apps/api/tauri'; async function resumeThread(workspaceId: string, threadId: string) { const result = await invoke<{ messages: Message[] }>('thread_resume', { workspaceId, threadId, }); dispatch({ type: 'SET_MESSAGES', messages: result.messages }); dispatch({ type: 'SET_UNREAD', unread: 0 }); } ``` Backend (`src-tauri/src/codex/mod.rs`): ```rust #[tauri::command] async fn thread_resume( workspace_id: String, thread_id: String, state: tauri::State<'_, AppState>, ) -> Result<serde_json::Value, String> { let manager = state.workspace_manager.lock().await; let workspace = manager.get_workspace(&workspace_id) .ok_or("Workspace not found")?; let server = state.codex_servers.lock().await .get(&workspace_id) .ok_or("Server not running")?; server.call("thread/resume", json!({ "thread_id": thread_id })).await .map_err(|e| e.to_string()) } ``` ### Sending a Message ```typescript async function sendMessage( workspaceId: string, threadId: string, content: string, attachments?: { path: string; mime_type: string }[] ) { await invoke('thread_send_message', { workspaceId, threadId, message: { role: 'user', content, attachments, }, }); } ``` ### Thread Lifecycle Commands ```typescript // Stop in-flight turn await invoke('thread_interrupt', { workspaceId, threadId }); // Pin thread await invoke('thread_pin', { workspaceId, threadId, pinned: true }); // Rename thread await invoke('thread_rename', { workspaceId, threadId, name: 'New Name' }); // Archive thread await invoke('thread_archive', { workspaceId, threadId }); // Copy thread (clone messages) await invoke('thread_copy', { workspaceId, threadId }); ``` ## Worktree Agents Worktree agents create isolated git worktrees under `<app-data>/worktrees/<workspace-id>/`. ### Creating a Worktree Frontend: ```typescript const worktree = await invoke<{ path: string; branch: string }>('worktree_create', { workspaceId: 'workspace-id', branch: 'feature-branch', }); console.log(`Worktree created at ${worktree.path}`); ``` Backend (`src-tauri/src/shared/workspaces_core/worktree.rs`): ```rust pub async fn create_worktree( workspace_path: &str, workspace_id: &str, branch: &str, app_data_dir: &Path, ) -> Result<Worktree, WorktreeError> { let worktree_dir = app_data_dir.join("worktrees").join(workspace_id); std::fs::create_dir_all(&worktree_dir)?; let worktree_path = worktree_dir.join(branch); let output = Command::new("git") .args(&["worktree", "add", worktree_path.to_str().unwrap(), branch]) .current_dir(workspace_path) .output()?; if !output.status.success() { return Err(WorktreeError::GitError( String::from_utf8_lossy(&output.stderr).to_string() )); } Ok(Worktree { path: worktree_path.to_string_lossy().to_string(), branch: branch.to_string(), }) } ``` ### Listing Worktrees ```typescript const worktrees = await invoke<Worktree[]>('worktree_list', { workspaceId: 'workspace-id', }); ``` ### Removing a Worktree ```typescript await invoke('worktree_remove', { workspaceId: 'workspace-id', path: '/path/to/worktree', }); ``` ## Git Integration ### Git Diff Stats Frontend: ```typescript const stats = await invoke<{ staged: { path: string; status: string }[]; unstaged: { path: string; status: string }[]; }>('git_diff_stats', { workspaceId: 'workspace-id' }); ``` Backend (`src-tauri/src/shared/git_ui_core/diff.rs`): ```rust pub fn get_diff_stats(repo_path: &str) -> Result<DiffStats, GitError> { let repo = Repository::open(repo_path)?; let mut index = repo.index()?; let head_tree = repo.head()?.peel_to_tree()?; let diff_index_tree = repo.diff_tree_to_index(Some(&head_tree), Some(&index), None)?; let diff_index_workdir = repo.diff_index_to_workdir(Some(&index), None)?; let staged = collect_diff_entries(&diff_index_tree)?; let unstaged = collect_diff_entries(&diff_index_workdir)?; Ok(DiffStats { staged, unstaged }) } ``` ### Branch Management ```typescript // List branches const branches = await invoke<{ name: string; current: boolean; ahead: number; behind: number }[]>( 'git_list_branches', { workspaceId: 'workspace-id' } ); // Checkout branch await invoke('git_checkout_branch', { workspaceId: 'workspace-id', branch: 'main', }); // Create branch await invoke('git_create_branch', { workspaceId: 'workspace-id', branch: 'feature-new', fromBranch: 'main', }); ``` ### GitHub Integration Requires `gh` CLI: ```typescript // List issues const issues = await invoke<GitHubIssue[]>('github_list_issues', { workspaceId: 'workspace-id', }); // List PRs const prs = await invoke<GitHubPR[]>('github_list_prs', { workspaceId: 'workspace-id', }); // Get PR diff const diff = await invoke<string>('github_pr_diff', { workspaceId: 'workspace-id', prNumber: 42, }); // Ask PR (send PR context to new thread) await invoke('github_ask_pr', {
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen