Skip to content

Conversation

@konard
Copy link
Member

@konard konard commented Dec 31, 2025

Summary

Fixes #141 - Apostrophes in text arguments are over-escaped, appearing as triple quotes (''') when stored literally by receiving programs.

Problem

When passing text containing apostrophes through command-stream, the default quote() function uses Bash's '\'' escaping pattern. While this is correct for shell interpretation, when the receiving program (like gh CLI) passes text to an API that stores it literally, the escape sequences appear as visible characters (''').

For example:

const notes = "Fix bug when dependencies didn't exist";
await $`gh release create --notes ${notes}`;
// GitHub displays: "Fix bug when dependencies didn'''t exist"

Solution

Add two new functions:

  • literal(value) - Mark text for double-quote escaping, preserving apostrophes while still escaping shell-dangerous characters ($, `, \, ")
  • quoteLiteral(value) - Low-level function for manual command building

Usage

import { $, literal } from 'command-stream';

// Problem: default escaping converts apostrophes
await $`gh release create --notes ${text}`;  // didn't → appears as didn'''t

// Solution: use literal() to preserve apostrophes
await $`gh release create --notes ${literal(text)}`;  // didn't stays didn't

How literal() differs from raw() and default quoting:

Function Apostrophe ' Shell Chars $ \ "` Safety Level
Default '\'' escaped Safely quoted ✅ Maximum
literal() Preserved Escaped ✅ Safe
raw() Preserved NOT escaped ⚠️ Dangerous

Changes

Core Implementation

  • js/src/$.mjs: Add quoteLiteral() and literal() functions with proper double-quote escaping

Tests

  • js/tests/$.test.mjs: Add 15 new tests covering:
    • quoteLiteral() unit tests (apostrophes, double quotes, dollar signs, backticks, backslashes, empty strings, null/undefined, safe strings, arrays)
    • literal() unit tests
    • Integration tests with shell execution

Documentation

  • README.md: Add comprehensive documentation for literal() and quoteLiteral(), including comparison table and usage examples

Case Study

  • docs/case-studies/issue-141/README.md: Detailed case study with:
    • Root cause analysis
    • Experimental evidence
    • Solution comparison

Experiments

  • experiments/test-literal-function.mjs: Verification script for the fix
  • experiments/test-apostrophe-escaping.mjs: Original issue reproduction
  • experiments/test-shell-escaping-detailed.mjs: Deep dive into escaping behavior
  • experiments/test-gh-simulation-v2.mjs: Simulation of gh CLI behavior

Configuration

  • eslint.config.js: Add experiments/ to lenient lint rules

Test Results

All tests pass:

 85 pass
 0 fail
 149 expect() calls

The new literal() function correctly preserves all test cases:

  • ✅ Basic apostrophe: "didn't"
  • ✅ Multiple apostrophes: "it's user's choice"
  • ✅ Double quotes: 'text is "quoted"'
  • ✅ Mixed quotes: 'it's "great"'
  • ✅ Backticks: "use `npm install`"
  • ✅ Newlines: "Line 1\nLine 2"
  • ✅ Dollar sign: "price is $100"
  • ✅ Backslashes: "path\to\file"

🤖 Generated with Claude Code

Adding CLAUDE.md with task information for AI processing.
This file will be removed when the task is complete.

Issue: #141
@konard konard self-assigned this Dec 31, 2025
Fixes #141 - Apostrophes in text arguments are over-escaped, appearing
as triple quotes (''') when stored literally by receiving programs.

## Problem

When passing text containing apostrophes through command-stream, the
default quote() function uses Bash's '\'' escaping pattern. While
correct for shell interpretation, when the receiving program (like gh
CLI) passes text to an API that stores it literally, the escape
sequences appear as visible characters (''').

## Solution

Add two new functions:
- `literal(value)` - Mark text for double-quote escaping, preserving
  apostrophes while still escaping shell-dangerous characters ($, `, \, ")
- `quoteLiteral(value)` - Low-level function for manual command building

## Usage

```javascript
import { $, literal } from 'command-stream';

// Problem: default escaping
await $`gh release create --notes ${text}`;  // didn't → didn'''t

// Solution: use literal()
await $`gh release create --notes ${literal(text)}`;  // didn't stays didn't
```

## Changes

- js/src/$.mjs: Add quoteLiteral() and literal() functions
- js/tests/$.test.mjs: Add 15 new tests for quoteLiteral and literal
- README.md: Add documentation for literal() and quoteLiteral()
- docs/case-studies/issue-141/: Case study with root cause analysis
- experiments/: Test scripts for verification
- eslint.config.js: Add experiments/ to lenient rules

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@konard konard changed the title [WIP] Apostrophes in text arguments are over-escaped, appearing as triple quotes (''') when stored literally Add literal() function to preserve apostrophes in shell arguments Dec 31, 2025
@konard konard marked this pull request as ready for review December 31, 2025 09:03
konard and others added 4 commits December 31, 2025 10:04
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@konard
Copy link
Member Author

konard commented Dec 31, 2025

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Public pricing estimate: $12.105426 USD
  • Calculated by Anthropic: $8.401264 USD
  • Difference: $-3.704162 (-30.60%)
    📎 Log file uploaded as GitHub Gist (2022KB)
    🔗 View complete solution draft log

Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Member Author

konard commented Dec 31, 2025

🔄 Auto-restart 1/3

Detected uncommitted changes from previous run. Starting new session to review and commit them.

Uncommitted files:

D temp-unicode-test.txt

Auto-restart will stop after changes are committed or after 2 more iterations. Please wait until working session will end and give your feedback.

@konard
Copy link
Member Author

konard commented Dec 31, 2025

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Public pricing estimate: $1.406471 USD
  • Calculated by Anthropic: $0.832822 USD
  • Difference: $-0.573649 (-40.79%)
    📎 Log file uploaded as GitHub Gist (2266KB)
    🔗 View complete solution draft log

Now working session is ended, feel free to review and add any feedback on the solution draft.

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.

Apostrophes in text arguments are over-escaped, appearing as triple quotes (''') when stored literally

2 participants