Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
63 changes: 63 additions & 0 deletions COMMAND_STREAM_ISSUE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Command-Stream JSON Quoting Issue Tracking

## Issue Summary
The `claude-profiles.mjs` script cannot use command-stream for macOS keychain storage commands due to automatic quote wrapping that corrupts JSON data.

## Affected Code Location
- **File**: `claude-profiles.mjs`
- **Line**: 387-391 (commented out code)
- **Function**: `setKeychainCredentials()`

## Problem Description
Command-stream's `$` template function automatically wraps interpolated values in single quotes, which corrupts JSON strings:

```javascript
// ❌ Current issue (line 387-391 in claude-profiles.mjs)
const result = await $`security add-generic-password -U -a $USER -s "Claude Code-credentials" -w "${escapedJson}"`;
// Results in: '{"claudeAiOauth":{...}}' ← Invalid JSON with wrapping quotes
// Expected: {"claudeAiOauth":{...}} ← Valid JSON
```

## Current Workaround
Using Node.js `execSync` instead (lines 372-376):
```javascript
const command = `security add-generic-password -U -a $USER -s "Claude Code-credentials" -w "${escapedJson}"`;
execSync(command, { shell: true, stdio: isVerbose ? 'inherit' : 'ignore' });
```

## Upstream Issue Tracking
- **Repository**: https://github.com/link-foundation/command-stream
- **Issue #39**: JSON strings with quotes cause escaping issues
- **Issue #45**: Automatic quote addition in interpolation causes issues
- **Status**: OPEN (as of 2025-09-10)
- **Our Comment**: https://github.com/link-foundation/command-stream/issues/39#issuecomment-3275976359

## Monitoring for Fix
To check if the issue has been resolved:

1. **Check issue status**: Visit issue #39 and #45 on the command-stream repository
2. **Test with reproduction script**: Run `examples/command-stream-json-issue-reproduction.mjs`
3. **Check for new releases**: Monitor https://www.npmjs.com/package/command-stream for versions > 0.7.0

## How to Update When Fixed
When the issue is resolved:

1. Uncomment the command-stream code (lines 387-391)
2. Remove or comment the execSync workaround (lines 372-376)
3. Test with various JSON payloads to ensure reliability
4. Update the version constraint in the use() call if needed

## Testing the Fix
Use the reproduction script to verify the fix:
```bash
node examples/command-stream-json-issue-reproduction.mjs
```

Expected output after fix:
- JSON should not be wrapped in extra single quotes
- Command output should show valid JSON format
- No escaping issues with nested quotes

## Related Files
- `claude-profiles.mjs` - Main script with the affected code
- `examples/command-stream-json-issue-reproduction.mjs` - Test reproduction script
4 changes: 4 additions & 0 deletions claude-profiles.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,10 @@ async function setKeychainCredentials(credentials) {
*
* This makes the JSON invalid and Claude Code cannot parse it.
*
* ISSUE REPORTED: https://github.com/link-foundation/command-stream/issues/39
* MONITORING: Run `node examples/check-command-stream-fix.mjs` to check for fix
* DOCUMENTATION: See COMMAND_STREAM_ISSUE.md for full details
*
* const result = await $`security add-generic-password -U -a $USER -s "Claude Code-credentials" -w "${escapedJson}"`.run({
* capture: true,
* mirror: false
Expand Down
82 changes: 82 additions & 0 deletions examples/check-command-stream-fix.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env node
// Script to check if command-stream JSON quoting issue has been fixed
// Run this periodically to monitor for fix availability

import { execSync } from 'child_process';

console.log('πŸ” Checking command-stream JSON quoting issue status...\n');

// Check if we can load command-stream
let commandStreamVersion;
try {
const { use } = eval(
await fetch('https://unpkg.com/use-m/use.js').then(r => r.text())
);
const [{ $ }] = await Promise.all([
use('command-stream@latest') // Use latest to check for fixes
]);

// Try to get version info
try {
const versionInfo = execSync('npm view command-stream version', {
encoding: 'utf8',
stdio: 'pipe'
}).trim();
commandStreamVersion = versionInfo;
console.log(`πŸ“¦ Command-stream version: ${commandStreamVersion}`);
} catch (e) {
console.log('πŸ“¦ Command-stream loaded (version unknown)');
}

// Test the JSON quoting issue
const testJson = '{"test": "value with \\"quotes\\""}';
const result = await $`echo "JSON: ${testJson}"`.run({
capture: true,
mirror: false
});

const output = result.stdout.trim();
const extractedJson = output.replace('JSON: ', '');

console.log('\nπŸ§ͺ Test Results:');
console.log('Input JSON :', testJson);
console.log('Output :', extractedJson);

// Check if JSON is properly handled (not wrapped in extra quotes)
let isFixed = false;
try {
// If it's properly handled, we should be able to parse it
const parsed = JSON.parse(extractedJson);
if (parsed && typeof parsed === 'object') {
isFixed = true;
console.log('βœ… FIXED: JSON is properly handled without extra quoting!');
console.log('πŸŽ‰ You can now update claude-profiles.mjs to use command-stream');
}
} catch (parseError) {
console.log('❌ NOT FIXED: JSON is still corrupted by extra quoting');
console.log(' Expected:', testJson);
console.log(' Got :', extractedJson);
}

// Additional checks
console.log('\nπŸ“Š Status:');
if (isFixed) {
console.log('🟒 Issue Status: RESOLVED');
console.log('πŸ”§ Action Needed: Update claude-profiles.mjs to use command-stream');
console.log('πŸ“ Update Instructions: See COMMAND_STREAM_ISSUE.md');
} else {
console.log('πŸ”΄ Issue Status: NOT YET RESOLVED');
console.log('⏳ Action Needed: Continue monitoring for fix');
console.log('πŸ”— Track: https://github.com/link-foundation/command-stream/issues/39');
}

} catch (error) {
console.error('❌ Error testing command-stream:', error.message);
console.log('\nπŸ’‘ This might indicate:');
console.log(' - Network connectivity issues');
console.log(' - Command-stream package issues');
console.log(' - Breaking changes in the library');
}

console.log('\nπŸ“… Last checked:', new Date().toISOString());
console.log('πŸ”„ Run this script periodically to monitor for fixes');
57 changes: 57 additions & 0 deletions examples/command-stream-json-issue-reproduction.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env node
// Minimal reproduction of command-stream JSON quoting issue affecting claude-profiles.mjs

// This is the same import used in claude-profiles.mjs
const { use } = eval(
await fetch('https://unpkg.com/use-m/use.js').then(r => r.text())
);
const [{ $ }] = await Promise.all([
use('command-stream@0.7.0')
]);

// Sample credentials data similar to what claude-profiles.mjs handles
const credentials = {
claudeAiOauth: {
accessToken: "sample_access_token_123",
refreshToken: "sample_refresh_token_456",
name: "Test User",
description: "User with \"quotes\" and 'apostrophes'"
}
};

console.log('Testing command-stream JSON interpolation issue...\n');

const jsonStr = JSON.stringify(credentials);
const escapedJson = jsonStr.replace(/"/g, '\\"'); // Escape quotes like in claude-profiles.mjs

console.log('Original JSON:', jsonStr);
console.log('Escaped JSON:', escapedJson);

try {
// This is the exact command that fails in claude-profiles.mjs line 387
console.log('\nTesting command-stream $ interpolation:');
const result = await $`echo "JSON would be: ${escapedJson}"`.run({
capture: true,
mirror: false
});

console.log('Command output:', result.stdout);
console.log('Expected: JSON with proper double quotes');
console.log('Actual: JSON wrapped in single quotes (invalid for parsing)');

// Show the difference
console.log('\nActual behavior:');
console.log('command-stream wraps the entire JSON in single quotes:');
console.log("'", result.stdout.trim().replace('JSON would be: ', ''), "'");

console.log('\nExpected behavior:');
console.log('JSON should remain as valid JSON without extra quoting:');
console.log(jsonStr);

} catch (error) {
console.error('Command failed:', error.message);
}

console.log('\nThis issue prevents claude-profiles.mjs from using command-stream');
console.log('for the keychain storage command at line 387.');
console.log('The workaround is using Node.js execSync instead.');