Skip to content

Can I auto encode string arguments to shell commands to simply gh operations? #301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
bhouston opened this issue Mar 15, 2025 · 5 comments · Fixed by #332
Closed

Can I auto encode string arguments to shell commands to simply gh operations? #301

bhouston opened this issue Mar 15, 2025 · 5 comments · Fixed by #332
Labels

Comments

@bhouston
Copy link
Member

I feel that I could auto encode end of line characters in string arguments.

I think the approaches are different on windows than on macOS and Linux.

But this would simplify the GitHub integration significantly.

@bhouston
Copy link
Member Author

Proposal for Auto-Encoding String Arguments in Shell Commands

Current Situation

Currently, when working with GitHub CLI commands that require multiline content (like issue or PR descriptions), the tool writes content to temporary files on disk and references them with --body-file. This approach works but is awkward and requires cleanup of temporary files.

The core issue is properly escaping string arguments with line endings across different operating systems (Windows, macOS, Linux) when passing them directly to shell commands.

Research on Available Solutions

After researching various approaches, I've identified several potential solutions:

1. Use existing shell escaping libraries

There are several npm packages that can help with shell escaping:

shell-escape (286 dependent projects)

  • Escapes and stringifies an array of arguments for shell execution
  • Simple API: shellescape(['echo', 'hello world', 'how\'s it going?'])
  • Last updated 10 years ago
  • May not handle Windows shell escaping properly

cross-spawn (10,000+ dependent projects)

  • Cross-platform solution for Node.js's child_process.spawn()
  • Handles Windows/POSIX differences automatically
  • More actively maintained
  • Focuses on argument handling rather than direct string escaping

escape-string-regexp

  • Not directly applicable as it's for RegExp escaping, not shell commands

2. Platform-specific approach

Shell escaping rules differ significantly between platforms:

  • POSIX (Linux/macOS): Uses single quotes to escape strings (doubling single quotes within the string)
  • Windows CMD: Uses carets (^) and double quotes differently
  • PowerShell: Has its own escaping rules with backticks

3. Base64 encoding approach

Instead of escaping, we could encode multiline content:

  1. Base64 encode the string content
  2. Pass the encoded string to the shell command
  3. Have the shell command decode it before use

Example with GitHub CLI:

const encodedContent = Buffer.from(content).toString('base64');
await shellExecute({
  command: `echo "${encodedContent}" | base64 -d | gh issue create --body-stdin`,
  description: 'Creating issue with encoded content'
});

This approach works consistently across platforms as base64 is standard.

Proposed Solution

I recommend implementing a combination of approaches:

  1. For simple commands: Use the cross-spawn package to handle argument escaping properly across platforms.

  2. For complex multiline content: Implement a pipe-based approach using base64 encoding:

    • Create a utility function executeWithEncodedInput(command, input, options)
    • The function encodes the input and pipes it to the command
    • Works with any command that accepts stdin input
  3. Add platform detection: Detect the current platform and use the appropriate escaping strategy:

    • For Windows: Use PowerShell's encoding capabilities when appropriate
    • For POSIX: Use base64 encoding through pipes

Implementation Example

import { spawn } from 'child_process';
import crossSpawn from 'cross-spawn';

// For simple commands with arguments that need escaping
function executeCommand(command: string, args: string[], options = {}) {
  return crossSpawn(command, args, { ...options, shell: true });
}

// For commands with complex multiline input
async function executeWithEncodedInput(command: string, input: string, options = {}) {
  const isWindows = process.platform === 'win32';
  
  if (isWindows) {
    // Windows-specific approach using PowerShell
    const encodedCommand = Buffer.from(input).toString('base64');
    return crossSpawn('powershell', [
      '-Command',
      `[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${encodedCommand}')) | ${command}`
    ], options);
  } else {
    // POSIX approach (Linux/macOS)
    const encodedInput = Buffer.from(input).toString('base64');
    const child = crossSpawn('bash', [
      '-c',
      `echo "${encodedInput}" | base64 -d | ${command}`
    ], options);
    
    return child;
  }
}

Benefits

  1. Cross-platform compatibility: Works consistently on Windows, macOS, and Linux
  2. No temporary files needed: Eliminates the need to create and clean up temporary files
  3. Handles all special characters: Base64 encoding preserves all characters, including newlines, quotes, etc.
  4. Simpler API: Provides a cleaner interface for tools that use shell commands

Next Steps

  1. Implement the proposed utility functions
  2. Add tests for various edge cases (special characters, very long strings, etc.)
  3. Update the GitHub integration to use the new approach
  4. Document the new functionality for users

Would you like me to proceed with implementing this solution?

@bhouston
Copy link
Member Author

Thank you for the research. I think that the best way to make this would and to have the tool parameters understandable intuitively by the LLM would be to add a parameter called stdinContent (a string) to the shellStart and shellExecute functions and describe it as content that is pipelined into the shell command upon start, or something to that extent. We can then use crossSpawn to start it in the cross platform way that you demonstrate above.

@bhouston
Copy link
Member Author

Updating the GitHub integration is really just changing the prompt used for githubMode so that it says use stdinContent for the body of GitHub issues, pull requests and comments rather than temporary files.

bhouston added a commit that referenced this issue Mar 20, 2025
This commit adds a new `stdinContent` parameter to the shellStart and shellExecute functions,
which allows passing content directly to shell commands via stdin. This is particularly useful
for GitHub CLI commands that accept stdin input.

Key changes:
- Add stdinContent parameter to shellStart and shellExecute
- Implement cross-platform approach using base64 encoding
- Update GitHub mode instructions to use stdinContent instead of temporary files
- Add tests for the new functionality
- Add example test files demonstrating usage

Closes #301
github-actions bot pushed a commit that referenced this issue Mar 20, 2025
# [mycoder-agent-v1.5.0](mycoder-agent-v1.4.2...mycoder-agent-v1.5.0) (2025-03-20)

### Bug Fixes

* improve resource trackers and fix tests ([c31546e](c31546e))
* properly format agentDone tool completion message ([8d19c41](8d19c41))
* resolve build and test issues ([549f0c7](549f0c7))
* resolve TypeError in interactive mode ([6e5e191](6e5e191))
* restore visibility of tool execution output ([0809694](0809694)), closes [#328](#328)
* shell message should reset output on each read ([670a10b](670a10b))
* update CLI cleanup to use ShellTracker instead of processStates ([3dca767](3dca767))

### Features

* add colored console output for agent logs ([5f38b2d](5f38b2d))
* Add interactive correction feature to CLI mode ([de2861f](de2861f)), closes [#326](#326)
* add parent-to-subagent communication in agentMessage tool ([3b11db1](3b11db1))
* add stdinContent parameter to shell commands ([5342a0f](5342a0f)), closes [#301](#301)
* implement ShellTracker to decouple from backgroundTools ([65378e3](65378e3))
* remove respawn capability, it wasn't being used anyhow. ([8e086b4](8e086b4))
Copy link

🎉 This issue has been resolved in version mycoder-agent-v1.5.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

github-actions bot pushed a commit that referenced this issue Mar 20, 2025
# [mycoder-v1.5.0](mycoder-v1.4.1...mycoder-v1.5.0) (2025-03-20)

### Bug Fixes

* list default model correctly in logging ([5b67b58](5b67b58))
* restore visibility of tool execution output ([0809694](0809694)), closes [#328](#328)
* update CLI cleanup to use ShellTracker instead of processStates ([3dca767](3dca767))

### Features

* Add interactive correction feature to CLI mode ([de2861f](de2861f)), closes [#326](#326)
* add stdinContent parameter to shell commands ([5342a0f](5342a0f)), closes [#301](#301)
Copy link

🎉 This issue has been resolved in version mycoder-v1.5.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant