fix-smart-quotes 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -16
- package/docs/plans/2026-01-21-hook-wrapper-fix.md +160 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,26 +4,28 @@ Convert straight quotes to proper typographic ("smart") quotes in Markdown files
|
|
|
4
4
|
|
|
5
5
|
| Language | Before | After |
|
|
6
6
|
|----------|--------|-------|
|
|
7
|
-
| German | `"text"` | `„text
|
|
8
|
-
| English | `"text"` |
|
|
7
|
+
| German | `"text"` | `„text“` (U+201E / U+201C) |
|
|
8
|
+
| English | `"text"` | `“text”` (U+201C / U+201D) |
|
|
9
9
|
|
|
10
10
|
## Why?
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Claude (including Claude.ai, Claude Desktop, and Claude Code) consistently uses straight quotes (`"`) instead of typographic quotes. This applies to both generated text and edits to existing content. The reason: straight quotes are universally compatible. They work in code, terminals, and forms, avoiding encoding issues that can turn smart quotes into question marks.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Other AI assistants behave differently: ChatGPT and DeepSeek typically output smart quotes, while Claude and Gemini use straight quotes.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
For prose and documentation, proper typography matters. German and English have distinct quote styles that convey professionalism and readability.
|
|
17
|
+
|
|
18
|
+
**Example - text with proper German quotes:**
|
|
17
19
|
```
|
|
18
|
-
Sie sagte: „Das ist wichtig
|
|
20
|
+
Sie sagte: „Das ist wichtig.“
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
**After
|
|
23
|
+
**After Claude edits or generates text:**
|
|
22
24
|
```
|
|
23
25
|
Sie sagte: "Das ist wichtig."
|
|
24
26
|
```
|
|
25
27
|
|
|
26
|
-
This tool restores the correct quotes
|
|
28
|
+
This tool restores the correct quotes: either manually via CLI or automatically after each Claude edit via hook.
|
|
27
29
|
|
|
28
30
|
## Installation
|
|
29
31
|
|
|
@@ -49,26 +51,37 @@ fix-smart-quotes docs/*.md
|
|
|
49
51
|
|
|
50
52
|
## Claude Code Hook
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
Automatically fix quotes after Claude edits Markdown files.
|
|
55
|
+
|
|
56
|
+
**1. Create wrapper script** at `~/.claude/hooks/fix-smart-quotes-wrapper.sh`:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
#!/bin/bash
|
|
60
|
+
INPUT=$(cat)
|
|
61
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
62
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
63
|
+
[[ ! "$FILE_PATH" =~ \.md$ ]] && exit 0
|
|
64
|
+
[ ! -f "$FILE_PATH" ] && exit 0
|
|
65
|
+
npx fix-smart-quotes "$FILE_PATH"
|
|
66
|
+
exit 0
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**2. Make executable:** `chmod +x ~/.claude/hooks/fix-smart-quotes-wrapper.sh`
|
|
53
70
|
|
|
54
|
-
Add to `~/.claude/settings.json`:
|
|
71
|
+
**3. Add to** `~/.claude/settings.json`:
|
|
55
72
|
|
|
56
73
|
```json
|
|
57
74
|
{
|
|
58
75
|
"hooks": {
|
|
59
76
|
"PostToolUse": [{
|
|
60
77
|
"matcher": "Write|Edit",
|
|
61
|
-
"hooks": [{
|
|
62
|
-
"type": "command",
|
|
63
|
-
"command": "npx fix-smart-quotes \"$FILE_PATH\"",
|
|
64
|
-
"timeout": 30
|
|
65
|
-
}]
|
|
78
|
+
"hooks": [{"type": "command", "command": "~/.claude/hooks/fix-smart-quotes-wrapper.sh", "timeout": 30}]
|
|
66
79
|
}]
|
|
67
80
|
}
|
|
68
81
|
}
|
|
69
82
|
```
|
|
70
83
|
|
|
71
|
-
|
|
84
|
+
> **Note:** Claude Code passes file paths via stdin JSON, not environment variables. The wrapper script handles this.
|
|
72
85
|
|
|
73
86
|
## Features
|
|
74
87
|
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Hook Wrapper Fix Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Fix the PostToolUse hook to correctly receive file paths via stdin JSON instead of environment variables.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Create a wrapper shell script that parses JSON from stdin, extracts the file path, filters for markdown files only, and calls fix-smart-quotes. Update settings.json to use the wrapper.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Bash, jq, Node.js (fix-smart-quotes)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Task 1: Create Wrapper Script
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Create: `~/.claude/hooks/fix-smart-quotes-wrapper.sh`
|
|
17
|
+
|
|
18
|
+
**Step 1: Create the wrapper script**
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
#!/bin/bash
|
|
22
|
+
# fix-smart-quotes-wrapper.sh
|
|
23
|
+
# Wrapper for fix-smart-quotes that parses Claude Code hook stdin JSON
|
|
24
|
+
|
|
25
|
+
# Read JSON from stdin
|
|
26
|
+
INPUT=$(cat)
|
|
27
|
+
|
|
28
|
+
# Extract file_path from JSON using jq
|
|
29
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty')
|
|
30
|
+
|
|
31
|
+
# Exit 0 if no file path found (non-blocking)
|
|
32
|
+
if [ -z "$FILE_PATH" ]; then
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Only process markdown files
|
|
37
|
+
if [[ ! "$FILE_PATH" =~ \.(md|markdown)$ ]]; then
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Check if file exists
|
|
42
|
+
if [ ! -f "$FILE_PATH" ]; then
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Run fix-smart-quotes
|
|
47
|
+
npx fix-smart-quotes "$FILE_PATH"
|
|
48
|
+
|
|
49
|
+
# Always exit 0 to not block Claude Code
|
|
50
|
+
exit 0
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Step 2: Make script executable**
|
|
54
|
+
|
|
55
|
+
Run: `chmod +x ~/.claude/hooks/fix-smart-quotes-wrapper.sh`
|
|
56
|
+
|
|
57
|
+
**Step 3: Verify script is executable**
|
|
58
|
+
|
|
59
|
+
Run: `ls -la ~/.claude/hooks/fix-smart-quotes-wrapper.sh`
|
|
60
|
+
Expected: `-rwxr-xr-x` permissions
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Task 2: Test Wrapper Script Manually
|
|
65
|
+
|
|
66
|
+
**Step 1: Test with valid markdown file path**
|
|
67
|
+
|
|
68
|
+
Run:
|
|
69
|
+
```bash
|
|
70
|
+
echo '{"tool_input": {"file_path": "/tmp/test.md"}}' > /tmp/test.md
|
|
71
|
+
echo 'This is a "test" file.' >> /tmp/test.md
|
|
72
|
+
echo '{"tool_input": {"file_path": "/tmp/test.md"}}' | ~/.claude/hooks/fix-smart-quotes-wrapper.sh
|
|
73
|
+
echo "Exit code: $?"
|
|
74
|
+
cat /tmp/test.md
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Expected: Exit code 0, file contains smart quotes
|
|
78
|
+
|
|
79
|
+
**Step 2: Test with non-markdown file**
|
|
80
|
+
|
|
81
|
+
Run:
|
|
82
|
+
```bash
|
|
83
|
+
echo '{"tool_input": {"file_path": "/tmp/test.json"}}' | ~/.claude/hooks/fix-smart-quotes-wrapper.sh
|
|
84
|
+
echo "Exit code: $?"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Expected: Exit code 0 (non-blocking)
|
|
88
|
+
|
|
89
|
+
**Step 3: Test with missing file_path**
|
|
90
|
+
|
|
91
|
+
Run:
|
|
92
|
+
```bash
|
|
93
|
+
echo '{"tool_input": {}}' | ~/.claude/hooks/fix-smart-quotes-wrapper.sh
|
|
94
|
+
echo "Exit code: $?"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Expected: Exit code 0 (non-blocking)
|
|
98
|
+
|
|
99
|
+
**Step 4: Cleanup test files**
|
|
100
|
+
|
|
101
|
+
Run: `rm -f /tmp/test.md /tmp/test.json`
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Task 3: Update settings.json
|
|
106
|
+
|
|
107
|
+
**Files:**
|
|
108
|
+
- Modify: `~/.claude/settings.json`
|
|
109
|
+
|
|
110
|
+
**Step 1: Update hook command**
|
|
111
|
+
|
|
112
|
+
Change from:
|
|
113
|
+
```json
|
|
114
|
+
"command": "npx fix-smart-quotes \"$FILE_PATH\""
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
To:
|
|
118
|
+
```json
|
|
119
|
+
"command": "~/.claude/hooks/fix-smart-quotes-wrapper.sh"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Step 2: Verify settings.json is valid JSON**
|
|
123
|
+
|
|
124
|
+
Run: `jq . ~/.claude/settings.json > /dev/null && echo "Valid JSON"`
|
|
125
|
+
Expected: "Valid JSON"
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Task 4: End-to-End Test
|
|
130
|
+
|
|
131
|
+
**Step 1: Create a test markdown file**
|
|
132
|
+
|
|
133
|
+
Run:
|
|
134
|
+
```bash
|
|
135
|
+
echo 'Test "quotes" here.' > /tmp/hook-test.md
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Step 2: Simulate hook invocation**
|
|
139
|
+
|
|
140
|
+
Run:
|
|
141
|
+
```bash
|
|
142
|
+
echo '{"tool_input": {"file_path": "/tmp/hook-test.md"}}' | ~/.claude/hooks/fix-smart-quotes-wrapper.sh
|
|
143
|
+
cat /tmp/hook-test.md
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Expected: File contains smart quotes (German style since no lang specified)
|
|
147
|
+
|
|
148
|
+
**Step 3: Cleanup**
|
|
149
|
+
|
|
150
|
+
Run: `rm /tmp/hook-test.md`
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Summary
|
|
155
|
+
|
|
156
|
+
After completing all tasks:
|
|
157
|
+
- [x] Wrapper script created at `~/.claude/hooks/fix-smart-quotes-wrapper.sh`
|
|
158
|
+
- [x] Wrapper parses stdin JSON, filters for .md files, exits 0 on non-matches
|
|
159
|
+
- [x] settings.json updated to use wrapper
|
|
160
|
+
- [x] Hook no longer causes errors on non-markdown files
|