8000 feat: add stdinContent parameter to shell commands by bhouston · Pull Request #332 · drivecore/mycoder · GitHub
[go: up one dir, main page]

Skip to content

feat: add stdinContent parameter to shell commands #332

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

Merged
merged 8 commits into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore: format code with prettier
Apply code formatting changes to test files:
- Fix formatting in test-gh-stdin.mjs
- Fix formatting in test-stdin-content.mjs
- Remove unused imports in shellExecute.test.ts
  • Loading branch information
bhouston committed Mar 20, 2025
commit a1dfb008e569cedbaa27591e744ddaa853d8e19b
18 changes: 0 additions & 18 deletions packages/agent/src/tools/shell/shellExecute.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { describe, expect, it, vi } from 'vitest';

import type { ToolContext } from '../../core/types';
import { shellExecuteTool } from './shellExecute';

// Skip testing for now

Check failure on line 5 in packages/agent/src/tools/shell/shellExecute.test.ts

View workflow job for this annotation

GitHub Actions / ci

'mockLogger' is assigned a value but never used. Allowed unused vars must match /^_/u
describe.skip('shellExecuteTool', () => {
const mockLogger = {
log: vi.fn(),
Expand All @@ -12,23 +11,6 @@
warn: vi.fn(),
info: vi.fn(),
};

// Create a mock ToolContext with all required properties
const mockToolContext: ToolContext = {
logger: mockLogger as any,
workingDirectory: '/test',
headless: false,
userSession: false,
pageFilter: 'none',
tokenTracker: { trackTokens: vi.fn() } as any,
githubMode: false,
provider: 'anthropic',
maxTokens: 4000,
temperature: 0,
agentTracker: { registerAgent: vi.fn() } as any,
shellTracker: { registerShell: vi.fn(), processStates: new Map() } as any,
browserTracker: { registerSession: vi.fn() } as any,
};

it('should execute a shell command', async () => {
// This is a dummy test that will be skipped
Expand Down
38 changes: 27 additions & 11 deletions packages/cli/test-gh-stdin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,32 @@
different indentation
- Shell operators: & | > < *
`;

Check failure on line 35 in packages/cli/test-gh-stdin.mjs

View workflow job for this annotation

GitHub Actions / ci

'console' is not defined
console.log('=== Testing GitHub CLI with stdinContent ===');

// Helper function to wait for all tests to complete

Check failure on line 38 in packages/cli/test-gh-stdin.mjs

View workflow job for this annotation

GitHub Actions / ci

'setTimeout' is not defined

Check failure on line 38 in packages/cli/test-gh-stdin.mjs

View workflow job for this annotation

GitHub Actions / ci

'wait' is assigned a value but never used. Allowed unused vars must match /^_/u
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// Helper function to execute a command with encoded content
const execWithEncodedContent = async (command, content, isWindows = process.platform === 'win32') => {
const execWithEncodedContent = async (
command,
content,

Check failure on line 44 in packages/cli/test-gh-stdin.mjs

View workflow job for this annotation

GitHub Actions / ci

'process' is not defined
isWindows = process.platform === 'win32',
) => {
return new Promise((resolve, reject) => {

Check failure on line 47 in packages/cli/test-gh-stdin.mjs

View workflow job for this annotation

GitHub Actions / ci

'Buffer' is not defined
const encodedContent = Buffer.from(content).toString('base64');
let cmd;

if (isWindows) {
// Windows approach using PowerShell
cmd = `powershell -Command "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${encodedContent}')) | ${command}"`;
} else {
// POSIX approach (Linux/macOS)
cmd = `echo "${encodedContent}" | base64 -d | ${command}`;
}

Check failure on line 58 in packages/cli/test-gh-stdin.mjs

View workflow job for this annotation

GitHub Actions / ci

'console' is not defined
console.log(`Executing command: ${cmd}`);

exec(cmd, (error, stdout, stderr) => {
if (error) {
reject(error);
Expand All @@ -64,19 +68,27 @@
});
};

// Test with temporary file approach (current method)

Check failure on line 71 in packages/cli/test-gh-stdin.mjs

View workflow job for this annotation

GitHub Actions / ci

'console' is not defined
console.log('\n=== Testing with temporary file approach ===');
const tempFile = path.join(os.tmpdir(), `test-gh-content-${Date.now()}.md`);
fs.writeFileSync(tempFile, issueContent);

Check failure on line 74 in packages/cli/test-gh-stdin.mjs

View workflow job for this annotation

GitHub Actions / ci

'console' is not defined
console.log(`Created temporary file: ${tempFile}`);

Check failure on line 75 in packages/cli/test-gh-stdin.mjs

View workflow job for this annotation

GitHub Actions / ci

'console' is not defined
console.log(`Command would be: gh issue create --title "Test Issue" --body-file "${tempFile}"`);
console.log('(Not executing actual GitHub command to avoid creating real issues)');
console.log(
`Command would be: gh issue create --title "Tes 10000 t Issue" --body-file "${tempFile}"`,
);
console.log(
'(Not executing actual GitHub command to avoid creating real issues)',
);

// Test with stdinContent approach (new method)
console.log('\n=== Testing with stdinContent approach ===');
console.log('Command would be: gh issue create --title "Test Issue" --body-stdin');
console.log(
'Command would be: gh issue create --title "Test Issue" --body-stdin',
);
console.log('With stdinContent parameter containing the issue content');
console.log('(Not executing actual GitHub command to avoid creating real issues)');
console.log(
'(Not executing actual GitHub command to avoid creating real issues)',
);

// Simulate the execution with a simple echo command
console.log('\n=== Simulating execution with echo command ===');
Expand All @@ -96,5 +108,9 @@
console.log('Temporary file removed');

console.log('\n=== Test completed ===');
console.log('The stdinContent approach successfully preserves all formatting and special characters');
console.log('This can be used with GitHub CLI commands that accept stdin input (--body-stdin flag)');
console.log(
'The stdinContent approach successfully preserves all formatting and special characters',
);
console.log(
'This can be used with GitHub CLI commands that accept stdin input (--body-stdin flag)',
);
37 changes: 22 additions & 15 deletions packages/cli/test-stdin-content.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,39 @@ const testStrings = [
'Simple string',
'String with spaces',
'String with "double quotes"',
'String with \'single quotes\'',
"String with 'single quotes'",
'String with $variable',
'String with `backticks`',
'String with newline\ncharacter',
'String with & and | operators',
'String with > redirect',
'String with * wildcard',
'Complex string with "quotes", \'single\', $var, `backticks`, \n, and special chars &|><*'
'Complex string with "quotes", \'single\', $var, `backticks`, \n, and special chars &|><*',
];

console.log('=== Testing stdinContent approaches ===');

// Helper function to wait for all tests to complete
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// Helper function to execute a command with encoded content
const execWithEncodedContent = async (command, content, isWindows = process.platform === 'win32') => {
const execWithEncodedContent = async (
command,
content,
isWindows = process.platform === 'win32',
) => {
return new Promise((resolve, reject) => {
const encodedContent = Buffer.from(content).toString('base64');
let cmd;

if (isWindows) {
// Windows approach using PowerShell
cmd = `powershell -Command "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${encodedContent}')) | ${command}"`;
} else {
// POSIX approach (Linux/macOS)
cmd = `echo "${encodedContent}" | base64 -d | ${command}`;
}

exec(cmd, (error, stdout, stderr) => {
if (error) {
reject(error);
Expand All @@ -52,7 +56,7 @@ const execWithEncodedContent = async (command, content, isWindows = process.plat
console.log('\n=== Testing Base64 encoding approach ===');
for (const str of testStrings) {
console.log(`\nOriginal: "${str}"`);

try {
// Test the encoded content approach
const { stdout } = await execWithEncodedContent('cat', str);
Expand All @@ -61,7 +65,7 @@ for (const str of testStrings) {
} catch (error) {
console.error(`Error: ${error.message}`);
}

// Add a small delay to ensure orderly output
await wait(100);
}
Expand All @@ -70,30 +74,33 @@ for (const str of testStrings) {
console.log('\n=== Comparing with temporary file approach ===');
for (const str of testStrings) {
console.log(`\nOriginal: "${str}"`);

// Create a temporary file with the content
const tempFile = path.join(os.tmpdir(), `test-content-${Date.now()}.txt`);
fs.writeFileSync(tempFile, str);

// Execute command using the temporary file
exec(`cat "${tempFile}"`, async (error, stdout, stderr) => {
console.log(`Output (temp file): "${stdout.trim()}"`);
console.log(`Success (temp file): ${stdout.trim() === str}`);

try {
// Test the encoded content approach
const { stdout: encodedStdout } = await execWithEncodedContent('cat', str);
const { stdout: encodedStdout } = await execWithEncodedContent(
'cat',
str,
);
console.log(`Output (encoded): "${encodedStdout.trim()}"`);
console.log(`Success (encoded): ${encodedStdout.trim() === str}`);
console.log(`Match: ${stdout.trim() === encodedStdout.trim()}`);
} catch (error) {
console.error(`Error: ${error.message}`);
}

// Clean up the temporary file
fs.unlinkSync(tempFile);
});

// Add a small delay to ensure orderly output
await wait(300);
}
}
Loading
0