# Plan
### Commit 1: `.gitignore` - Add standard Rust .gitignore ignoring the /target build directory. [COMPLETE]
### .add-gitignore
> **File**: `.gitignore`
> **Type**: NEW
> **Commit**: 1 of 1 for this file
#### Description
Add standard Rust .gitignore ignoring the /target build directory.
#### Diff
```diff
+/target
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| **Rule 3: Lines** | 1 lines | PASS |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 2: `Cargo.toml` - Add Cargo.toml with full crates.io metadata: package name json-escape-cli, binary name json-escape, and the json-escape = 0.3 crate dependency. [COMPLETE]
### Cargo.add-cargo-toml
> **File**: `Cargo.toml`
> **Type**: NEW
> **Commit**: 1 of 1 for this file
#### Description
Add Cargo.toml with full crates.io metadata: package name json-escape-cli, binary name json-escape, and the json-escape = 0.3 crate dependency.
#### Diff
```diff
+[package]
+name = "json-escape-cli"
+version = "0.1.0"
+edition = "2021"
+description = "A CLI tool to JSON-escape strings read from stdin"
+license = "MIT"
+repository = "https://github.com/nick-the-nuke/json-escape"
+readme = "README.md"
+keywords = ["json", "escape", "cli"]
+categories = ["command-line-utilities"]
+
+[[bin]]
+name = "json-escape"
+path = "src/main.rs"
+
+[dependencies]
+json-escape = "0.3"
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| **Rule 3: Lines** | 17 lines | BORDERLINE |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 3: `src/main.rs` - Add main.rs: reads all stdin, escapes with json_escape::escape_str, post-processes to replace / with \/ by default, wraps in double quotes, and writes to stdout. Supports --no-escape-slash flag to skip the forward slash substitution. [COMPLETE]
### src.main.add-main-rs
> **File**: `src/main.rs`
> **Type**: NEW
> **Commit**: 1 of 1 for this file
#### Description
Add main.rs: reads all stdin, escapes with json_escape::escape_str, post-processes to replace / with / by default, wraps in double quotes, and writes to stdout. Supports --no-escape-slash flag to skip the forward slash substitution.
#### Diff
```diff
+use std::io::{self, Read};
+
+fn main() {
+ let mut args = std::env::args().skip(1);
+ let mut no_escape_slash = false;
+ while let Some(arg) = args.next() {
+ if arg == "--no-escape-slash" {
+ no_escape_slash = true;
+ }
+ }
+ let mut input = String::new();
+ io::stdin().read_to_string(&mut input).expect("Failed to read stdin");
+ let escaped = json_escape::escape_str(&input);
+ let result = if no_escape_slash {
+ escaped.to_string()
+ } else {
+ escaped.replace('/', "\/")
+ };
+ println!(""{}"", result);
+}
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| Rule | Check | Status |
|------|-------|--------|
| **Rule 3: Lines** | 20 lines | BORDERLINE |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 4: `LICENSE` - Add MIT LICENSE file with year 2024 and author Nick — copyright header and grant of rights section. [COMPLETE]
### LICENSE.add-license-preamble
> **File**: `LICENSE`
> **Type**: NEW
> **Commit**: 1 of 1 for this file
#### Description
Add MIT LICENSE file with year 2024 and author Nick — copyright header and grant of rights section.
#### Diff
```diff
+MIT License
+
+Copyright (c) 2024 Nick
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| **Rule 3: Lines** | 13 lines | PASS |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 5: `LICENSE` - Append the warranty disclaimer section to LICENSE, completing the standard MIT license text. [COMPLETE]
### LICENSE.add-license-disclaimer
> **File**: `LICENSE`
> **Type**: MODIFIED
> **Commit**: 1 of 1 for this file
#### Description
Append the warranty disclaimer section to LICENSE, completing the standard MIT license text.
#### Diff
```diff
MIT License
...
copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| **Rule 3: Lines** | 8 lines | PASS |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 6: `README.md` - Add README.md with project title, description, installation instructions from GitHub releases, and basic usage showing stdin piping. [COMPLETE]
### README.add-readme-header
> **File**: `README.md`
> **Type**: NEW
> **Commit**: 1 of 1 for this file
#### Description
Add README.md with project title, description, installation instructions from GitHub releases, and basic usage showing stdin piping.
#### Diff
```diff
+# json-escape
+
+A CLI tool that reads a string from stdin and outputs it as a JSON-escaped string,
+wrapped in double quotes — ready to paste directly into JSON.
+
+## Installation
+
+Download the latest binary for your platform from the
+[GitHub Releases](https://github.com/nick-the-nuke/json-escape/releases) page.
+
+| Linux x86_64 | `json-escape-x86_64-unknown-linux-gnu.tar.gz` |
+| macOS x86_64 | `json-escape-x86_64-apple-darwin.tar.gz` |
+| macOS ARM64 | `json-escape-aarch64-apple-darwin.tar.gz` |
+| Windows x86_64 | `json-escape-x86_64-pc-windows-msvc.zip` |
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| **Rule 3: Lines** | 16 lines | PASS |
| **Rule 3: Exempt** | markdown | EXEMPT |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 7: `README.md` - Append usage examples section to README.md: basic stdin piping, the --no-escape-slash flag explanation with examples, and notes on default forward slash escaping behavior. [COMPLETE]
### README.add-readme-usage
> **File**: `README.md`
> **Type**: MODIFIED
> **Commit**: 1 of 1 for this file
#### Description
Append usage examples section to README.md: basic stdin piping, the --no-escape-slash flag explanation with examples, and notes on default forward slash escaping behavior.
#### Diff
```diff
...
+
+## Usage
+
+```
+```
+
+## Flags
+
+### `--no-escape-slash`
+
+By default, `json-escape` escapes forward slashes (`/` → `/`), which is safe
+for HTML/script embedding. Use `--no-escape-slash` to output `/` as-is:
+
+```
+echo "https://example.com" | json-escape
+"https://example.com"
+
+echo "https://example.com" | json-escape --no-escape-slash
+"https://example.com"
+```
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| **Rule 3: Lines** | 22 lines | PASS |
| **Rule 3: Exempt** | markdown | EXEMPT |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 8: `.github/workflows/ci.yml` - Add CI workflow YAML with the trigger configuration (push and pull_request on main), and the job matrix definition covering ubuntu-latest, macos-latest, and windows-latest. [COMPLETE]
### .github.workflows.ci.add-ci-workflow-header
> **File**: `.github/workflows/ci.yml`
> **Type**: NEW
> **Commit**: 1 of 1 for this file
#### Description
Add CI workflow YAML with the trigger configuration (push and pull_request on main), and the job matrix definition covering ubuntu-latest, macos-latest, and windows-latest.
#### Diff
```diff
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ check:
+ name: Check (${{ matrix.os }})
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ components: clippy, rustfmt
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| Rule | Check | Status |
|------|-------|--------|
| **Rule 3: Lines** | 20 lines | BORDERLINE |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 9: `.github/workflows/ci.yml` - Append the four CI step commands to .github/workflows/ci.yml: cargo check, cargo test, cargo clippy (deny warnings), and cargo fmt (check only). [COMPLETE]
### .github.workflows.ci.add-ci-workflow-steps
> **File**: `.github/workflows/ci.yml`
> **Type**: MODIFIED
> **Commit**: 1 of 1 for this file
#### Description
Append the four CI step commands to .github/workflows/ci.yml: cargo check, cargo test, cargo clippy (deny warnings), and cargo fmt (check only).
#### Diff
```diff
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
+ - name: cargo check
+ run: cargo check
+ - name: cargo test
+ run: cargo test
+ - name: cargo clippy
+ run: cargo clippy -- -D warnings
+ - name: cargo fmt
+ run: cargo fmt --check
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| **Rule 3: Lines** | 8 lines | PASS |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 10: `.github/workflows/release.yml` - Add release.yml triggered on GitHub release publish events, with a build job and a 5-target matrix: x86_64-unknown-linux-gnu (ubuntu-latest), aarch64-unknown-linux-gnu (ubuntu-latest cross-compile), x86_64-apple-darwin (macos-latest), aarch64-apple-darwin (macos-latest), and x86_64-pc-windows-msvc (windows-latest). [COMPLETE]
### .github.workflows.release.add-release-workflow-trigger
> **File**: `.github/workflows/release.yml`
> **Type**: NEW
> **Commit**: 1 of 0 for this file
#### Description
Add release.yml triggered on GitHub release publish events, with a build job and a 5-target matrix: x86_64-unknown-linux-gnu (ubuntu-latest), aarch64-unknown-linux-gnu (ubuntu-latest cross-compile), x86_64-apple-darwin (macos-latest), aarch64-apple-darwin (macos-latest), and x86_64-pc-windows-msvc (windows-latest).
#### Diff
```diff
+name: Release
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ build:
+ name: Build (${{ matrix.target }})
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ include:
+ - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
+ - { target: aarch64-unknown-linux-gnu, os: ubuntu-latest }
+ - { target: x86_64-apple-darwin, os: macos-latest }
+ - { target: aarch64-apple-darwin, os: macos-latest }
+ - { target: x86_64-pc-windows-msvc, os: windows-latest }
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| Rule | Check | Status |
|------|-------|--------|
| **Rule 3: Lines** | 18 lines | BORDERLINE |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 11: `.github/workflows/release.yml` - Append the checkout, Rust toolchain setup, and build steps to the release job using houseabsolute/actions-rust-cross@v1 for all targets (automatically uses cross for cross-compilation targets like aarch64-unknown-linux-gnu and cargo for native targets). Strip binary on non-Windows targets. [COMPLETE]
### .github.workflows.release.add-release-workflow-build-steps
> **File**: `.github/workflows/release.yml`
> **Type**: MODIFIED
> **Commit**: 1 of 0 for this file
#### Description
Append the checkout, Rust toolchain setup, and build steps to the release job using houseabsolute/actions-rust-cross@v1 for all targets (automatically uses cross for cross-compilation targets like aarch64-unknown-linux-gnu and cargo for native targets). Strip binary on non-Windows targets.
#### Diff
```diff
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ targets: ${{ matrix.target }}
+ - name: Build
+ uses: houseabsolute/actions-rust-cross@v1
+ with:
+ target: ${{ matrix.target }}
+ args: --release
+ - name: Strip binary (non-Windows)
+ if: ${{ !contains(matrix.target, 'windows') }}
+ run: strip target/${{ matrix.target }}/release/json-escape
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| **Rule 3: Lines** | 13 lines | PASS |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |
### Commit 12: `.github/workflows/release.yml` - Append the archive and GitHub release upload steps to release.yml: tar.gz for Unix targets, zip for Windows, then upload each archive to the release using gh release upload. [COMPLETE]
### .github.workflows.release.add-release-workflow-archive
> **File**: `.github/workflows/release.yml`
> **Type**: MODIFIED
> **Commit**: 1 of 1 for this file
#### Description
Append the archive and GitHub release upload steps to release.yml: tar.gz for Unix targets, zip for Windows, then upload each archive to the release using gh release upload.
#### Diff
```diff
+ - name: Archive (Unix)
+ if: ${{ !contains(matrix.target, 'windows') }}
+ run: |
+ cd target/${{ matrix.target }}/release
+ tar czf json-escape-${{ matrix.target }}.tar.gz json-escape
+ - name: Archive (Windows)
+ if: ${{ contains(matrix.target, 'windows') }}
+ run: |
+ cd target/${{ matrix.target }}/release
+ Compress-Archive json-escape.exe json-escape-${{ matrix.target }}.zip
+ - name: Upload to release
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh release upload ${{ github.ref_name }} + target/${{ matrix.target }}/release/json-escape-${{ matrix.target }}.*
```
#### Rule Compliance
> See Operating Procedures for Rules 3-4
| Rule | Check | Status |
|------|-------|--------|
| **Rule 3: Lines** | 16 lines | BORDERLINE |
| **Rule 3: Exempt** | N/A | N/A |
| **Rule 4: Atomic** | Single logical unit | YES |