Skip to content

Conversation

@ThomasK33
Copy link
Member

Add complete workspace scripts feature with runtime-aware discovery, execution, and auto-completion for both local and SSH workspaces.

Script Discovery

  • New listScripts() function uses Runtime interface instead of local fs
  • Works with both local and SSH workspaces via execBuffered()
  • Extracts descriptions from # Description: or # @description comments
  • Adds WORKSPACE_LIST_SCRIPTS IPC handler
  • Includes unit tests with mocked runtime

Script Execution

  • New /script and /s slash commands with tab completion
  • WORKSPACE_EXECUTE_SCRIPT IPC handler using bash tool
  • Runtime-aware script existence checking via runtime.stat()
  • Scripts run in workspace directory with project secrets
  • 5-minute default timeout

Environment Variables

  • CMUX_OUTPUT: Write markdown for custom toast display
  • CMUX_PROMPT: Send follow-up message to agent after script runs

UI/UX

  • Script execution shows toast with exit code
  • Custom toast content from CMUX_OUTPUT (10KB limit)
  • Auto-send CMUX_PROMPT content as user message (100KB limit)
  • Command palette integration for script selection
  • Tab completion in chat input

Documentation

  • Add docs/scripts.md with usage examples
  • Demo scripts in .cmux/scripts/
  • Storybook story for script execution flow

Generated with cmux

@chatgpt-codex-connector
Copy link

💡 Codex Review

cmux/src/services/ipcMain.ts

Lines 929 to 1016 in 9af9019

// Create scoped temp directory for this IPC call
using tempDir = new DisposableTempDir("cmux-ipc-script");
// Create environment files for script output and prompts
const outputFile = path.join(tempDir.path, "output.txt");
const promptFile = path.join(tempDir.path, "prompt.txt");
// Ensure files exist (empty) before script runs
await fsPromises.writeFile(outputFile, "", "utf-8");
await fsPromises.writeFile(promptFile, "", "utf-8");
// Create runtime and compute workspace path
const runtimeConfig = metadata.runtimeConfig ?? {
type: "local" as const,
srcBaseDir: this.config.srcDir,
};
const runtime = createRuntime(runtimeConfig);
const workspacePath = runtime.getWorkspacePath(metadata.projectPath, metadata.name);
// Get script path and verify it exists via runtime (works for both local and SSH)
const scriptPath = getScriptPath(workspacePath, scriptName);
let scriptExists = false;
try {
const stat = await runtime.stat(scriptPath);
scriptExists = !stat.isDirectory; // Must be a file, not directory
} catch {
// File doesn't exist or can't be accessed
scriptExists = false;
}
if (!scriptExists) {
return Err(
`Script not found: .cmux/scripts/${scriptName}. Create the script in your workspace and make it executable (chmod +x).`
);
}
// Note: We don't check executable bit here - bash will fail with clear error if not executable
// This is intentional because SSH doesn't reliably expose permission bits via stat()
// Build command with arguments
const escapedArgs = args.map((arg) => `"${arg.replace(/"/g, '\\"')}"`).join(" ");
const command = `"${scriptPath}"${escapedArgs ? ` ${escapedArgs}` : ""}`;
// Create bash tool to execute the script with env files
const bashTool = createBashTool({
cwd: workspacePath,
runtime,
secrets: secretsToRecord(projectSecrets),
runtimeTempDir: tempDir.path,
overflow_policy: "truncate",
env: {
CMUX_OUTPUT: outputFile,
CMUX_PROMPT: promptFile,
},
});
// Execute the script with 5 minute timeout by default
const result = (await bashTool.execute!(
{
script: command,
timeout_secs: 300,
},
{
toolCallId: `script-${scriptName}-${Date.now()}`,
messages: [],
}
)) as BashToolResult;
// Read environment files after execution
const MAX_OUTPUT_SIZE = 10 * 1024; // 10KB
const MAX_PROMPT_SIZE = 100 * 1024; // 100KB
let outputContent = "";
try {
outputContent = await fsPromises.readFile(outputFile, "utf-8");
if (outputContent.length > MAX_OUTPUT_SIZE) {
outputContent =
outputContent.substring(0, MAX_OUTPUT_SIZE) + "\n\n[Truncated - output too large]";
}
} catch {
// File doesn't exist or can't be read - ignore
}
let promptContent = "";
try {
promptContent = await fsPromises.readFile(promptFile, "utf-8");
if (promptContent.length > MAX_PROMPT_SIZE) {

P1 Badge Use runtime temp paths for CMUX_OUTPUT/CMUX_PROMPT files

The script IPC handler writes CMUX_OUTPUT and CMUX_PROMPT files into a local DisposableTempDir and then exposes those paths to the script via environment variables. This works for local workspaces, but for SSH-backed workspaces the script runs on the remote machine and the /tmp/cmux-ipc-script-* directory is never created there, so any script that tries to write to CMUX_OUTPUT or CMUX_PROMPT will fail with “No such file or directory” and the custom toast/prompt features never work. To support the advertised SSH runtimes, the handler should provision the temp files through the runtime (e.g. runtime.writeFile/runtime.exec('mktemp')) or copy the files after execution instead of passing local paths directly.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
Copy link
Member Author

@codex addressed that feedback by provisioning CMUX_OUTPUT/CMUX_PROMPT via runtime temp directories and adding runtimeScriptExecution integration test.

@chatgpt-codex-connector
Copy link

To use Codex here, create an environment for this repo.

@ThomasK33
Copy link
Member Author

@codex address that feedback

@chatgpt-codex-connector
Copy link

To use Codex here, create an environment for this repo.

@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +31 to +34
const listResult = await execBuffered(
runtime,
`find "${scriptsDir}" -maxdepth 1 -type f -printf '%f\\n' 2>/dev/null | sort || true`,
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Replace GNU-only find usage when listing scripts

listScripts calls find "${scriptsDir}" -printf '%f' to enumerate .cmux/scripts. The -printf flag only exists in GNU find; the BSD find shipped with macOS errors and returns no filenames, so script discovery and slash-command suggestions will be empty for Mac developers even when scripts exist. Use a portable approach (e.g., find ... -print combined with basename, or perform the traversal via the Runtime API) so discovery works across platforms.

Useful? React with 👍 / 👎.

Comment on lines +88 to 90
// allowedHosts: devServerHost === "0.0.0.0" ? undefined : ["localhost", "127.0.0.1"],
allowedHosts: ["coderbook-pro.manakin-ray.ts.net"],
sourcemapIgnoreList: () => false, // Show all sources in DevTools

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Dev server host check blocks localhost connections

vite.config.ts now hardcodes server.allowedHosts to ["coderbook-pro.manakin-ray.ts.net"] and removes the previous localhost list. Running the dev server on a typical machine will produce “Invalid Host header” for localhost or 127.0.0.1, making local development impossible unless the browser connects through that single domain. Keep the loopback hosts in the list (or restore the prior conditional) while adding the extra host so local dev remains usable.

Useful? React with 👍 / 👎.

Add complete workspace scripts feature with runtime-aware discovery,
execution, and auto-completion for both local and SSH workspaces.

**Script Discovery:**
- New listScripts() function uses Runtime interface instead of local fs
- Works with both local and SSH workspaces via execBuffered()
- Extracts descriptions from # Description: or # @description comments
- Adds WORKSPACE_LIST_SCRIPTS IPC handler
- Includes unit tests with mocked runtime

**Script Execution:**
- New /script and /s slash commands with tab completion
- WORKSPACE_EXECUTE_SCRIPT IPC handler using bash tool
- Runtime-aware script existence checking via runtime.stat()
- Scripts run in workspace directory with project secrets
- 5-minute default timeout

**Environment Variables:**
- CMUX_OUTPUT: Write markdown for custom toast display
- CMUX_PROMPT: Send follow-up message to agent after script runs

**UI/UX:**
- Script execution shows toast with exit code
- Custom toast content from CMUX_OUTPUT (10KB limit)
- Auto-send CMUX_PROMPT content as user message (100KB limit)
- Command palette integration for script selection
- Tab completion in chat input

**Documentation:**
- Add docs/scripts.md with usage examples
- Demo scripts in .cmux/scripts/
- Storybook story for script execution flow

Generated with cmux

Change-Id: I301cff2ec5551b4b1a08d41be84c363dfbf13f72
Signed-off-by: Test <test@example.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant