vietnam-cli 1.0.0
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/LICENSE +22 -0
- package/README.md +98 -0
- package/bin/cli.js +85 -0
- package/package.json +52 -0
- package/src/index.js +218 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# vietnam-cli
|
|
2
|
+
|
|
3
|
+
Fix Vietnamese input method (Unikey, EVKey, etc.) for CLI tools like **Auggie CLI** and **Claude Code**.
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
Vietnamese IME (Input Method Editor) like Unikey or EVKey uses a technique where it sends a DEL character (`\x7F`) to delete the previous character, then inserts the new accented character.
|
|
8
|
+
|
|
9
|
+
Some CLI tools incorrectly interpret `\x7F` as the "Delete" key (forward delete) instead of "Backspace" (backward delete), causing Vietnamese input to malfunction.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g vietnam-cli
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Apply patch to all supported tools
|
|
20
|
+
```bash
|
|
21
|
+
vietnam-cli patch
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Check patch status
|
|
25
|
+
```bash
|
|
26
|
+
vietnam-cli check
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Patch specific tool
|
|
30
|
+
```bash
|
|
31
|
+
vietnam-cli patch auggie # Patch only Auggie CLI
|
|
32
|
+
vietnam-cli patch claude # Patch only Claude Code
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Restore original files
|
|
36
|
+
```bash
|
|
37
|
+
vietnam-cli restore
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Show help
|
|
41
|
+
```bash
|
|
42
|
+
vietnam-cli help
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Supported Tools
|
|
46
|
+
|
|
47
|
+
| Tool | Package | Status |
|
|
48
|
+
|------|---------|--------|
|
|
49
|
+
| Auggie CLI | `@augmentcode/auggie` | ✅ Supported |
|
|
50
|
+
| Claude Code | `@anthropic-ai/claude-code` | ✅ Supported |
|
|
51
|
+
|
|
52
|
+
## How It Works
|
|
53
|
+
|
|
54
|
+
The patch modifies the key parsing code in the CLI tools:
|
|
55
|
+
|
|
56
|
+
**Before (broken):**
|
|
57
|
+
```javascript
|
|
58
|
+
else if(e==="\x7F"||e==="\x1B\x7F")n.name="delete"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**After (fixed):**
|
|
62
|
+
```javascript
|
|
63
|
+
else if(e==="\x7F"||e==="\x1B\x7F")n.name="backspace"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
This change makes the `\x7F` character behave as backspace, which is what Vietnamese IME expects.
|
|
67
|
+
|
|
68
|
+
## Important Notes
|
|
69
|
+
|
|
70
|
+
⚠️ **After updating the CLI tools** (e.g., `npm update -g @augmentcode/auggie`), you need to **re-apply the patch**:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
vietnam-cli patch
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Programmatic Usage
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
const { patch, check, restore } = require('vietnam-cli');
|
|
80
|
+
|
|
81
|
+
// Apply patch
|
|
82
|
+
await patch('auggie');
|
|
83
|
+
|
|
84
|
+
// Check status
|
|
85
|
+
await check('all');
|
|
86
|
+
|
|
87
|
+
// Restore original
|
|
88
|
+
await restore('auggie');
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT
|
|
94
|
+
|
|
95
|
+
## Contributing
|
|
96
|
+
|
|
97
|
+
Issues and PRs are welcome at [GitHub](https://github.com/user/vietnam-cli).
|
|
98
|
+
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vietnamese IME Patch CLI
|
|
5
|
+
* Fix Vietnamese input (Unikey/EVKey) for CLI tools
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { patch, check, restore, findTargets } = require('../src/index.js');
|
|
9
|
+
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
const command = args[0] || 'help';
|
|
12
|
+
const target = args[1] || 'all';
|
|
13
|
+
|
|
14
|
+
const HELP_TEXT = `
|
|
15
|
+
Vietnamese IME Patch - Fix Vietnamese input for CLI tools
|
|
16
|
+
==========================================================
|
|
17
|
+
|
|
18
|
+
Usage: vietnam-cli <command> [target]
|
|
19
|
+
|
|
20
|
+
Commands:
|
|
21
|
+
patch [target] Apply Vietnamese IME fix (default: all)
|
|
22
|
+
check [target] Check if patch is applied
|
|
23
|
+
restore [target] Restore original files from backup
|
|
24
|
+
list List all supported targets and their status
|
|
25
|
+
help Show this help message
|
|
26
|
+
|
|
27
|
+
Targets:
|
|
28
|
+
all All supported CLI tools (default)
|
|
29
|
+
auggie Auggie CLI (@augmentcode/auggie)
|
|
30
|
+
claude Claude Code CLI (@anthropic-ai/claude-code)
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
vietnam-cli patch # Patch all supported tools
|
|
34
|
+
vietnam-cli patch auggie # Patch only auggie-cli
|
|
35
|
+
vietnam-cli check # Check patch status
|
|
36
|
+
vietnam-cli restore auggie # Restore auggie to original
|
|
37
|
+
|
|
38
|
+
What this fixes:
|
|
39
|
+
Vietnamese IME (Unikey, EVKey, etc.) sends DEL character (\\x7F) to delete
|
|
40
|
+
the previous character before inserting accented characters. Some CLI tools
|
|
41
|
+
incorrectly treat \\x7F as "delete" (forward delete) instead of "backspace".
|
|
42
|
+
This patch fixes that behavior.
|
|
43
|
+
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
async function main() {
|
|
47
|
+
switch (command) {
|
|
48
|
+
case 'patch':
|
|
49
|
+
console.log('🔧 Applying Vietnamese IME patch...\n');
|
|
50
|
+
await patch(target);
|
|
51
|
+
break;
|
|
52
|
+
|
|
53
|
+
case 'check':
|
|
54
|
+
console.log('🔍 Checking Vietnamese IME patch status...\n');
|
|
55
|
+
await check(target);
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
case 'restore':
|
|
59
|
+
console.log('↩️ Restoring original files...\n');
|
|
60
|
+
await restore(target);
|
|
61
|
+
break;
|
|
62
|
+
|
|
63
|
+
case 'list':
|
|
64
|
+
console.log('📋 Supported targets:\n');
|
|
65
|
+
await findTargets();
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
case 'help':
|
|
69
|
+
case '--help':
|
|
70
|
+
case '-h':
|
|
71
|
+
console.log(HELP_TEXT);
|
|
72
|
+
break;
|
|
73
|
+
|
|
74
|
+
default:
|
|
75
|
+
console.error(`Unknown command: ${command}`);
|
|
76
|
+
console.log(HELP_TEXT);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
main().catch(err => {
|
|
82
|
+
console.error('Error:', err.message);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
});
|
|
85
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vietnam-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Fix Vietnamese IME input (Unikey/EVKey) for CLI tools like auggie-cli and claude-code",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vietnam-cli": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node bin/cli.js check",
|
|
11
|
+
"patch": "node bin/cli.js patch",
|
|
12
|
+
"check": "node bin/cli.js check",
|
|
13
|
+
"restore": "node bin/cli.js restore",
|
|
14
|
+
"prepublishOnly": "node scripts/prepare-publish.js",
|
|
15
|
+
"postpublish": "node scripts/restore-readme.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"vietnamese",
|
|
19
|
+
"ime",
|
|
20
|
+
"unikey",
|
|
21
|
+
"evkey",
|
|
22
|
+
"telex",
|
|
23
|
+
"vni",
|
|
24
|
+
"auggie",
|
|
25
|
+
"claude-code",
|
|
26
|
+
"input-method",
|
|
27
|
+
"keyboard",
|
|
28
|
+
"patch",
|
|
29
|
+
"fix",
|
|
30
|
+
"tieng-viet"
|
|
31
|
+
],
|
|
32
|
+
"author": "",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/user/vietnam-cli.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/user/vietnam-cli/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/user/vietnam-cli#readme",
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=14.0.0"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"bin/",
|
|
47
|
+
"src/",
|
|
48
|
+
"README.md",
|
|
49
|
+
"LICENSE"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
|
package/src/index.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vietnamese IME Patch
|
|
3
|
+
* Fix Vietnamese input (Unikey/EVKey) for CLI tools
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
|
|
10
|
+
// Patch configurations for different CLI tools
|
|
11
|
+
const PATCH_CONFIGS = {
|
|
12
|
+
auggie: {
|
|
13
|
+
name: 'Auggie CLI',
|
|
14
|
+
package: '@augmentcode/auggie',
|
|
15
|
+
file: 'augment.mjs',
|
|
16
|
+
patterns: [
|
|
17
|
+
{
|
|
18
|
+
original: 'else if(e==="\\x7F"||e==="\\x1B\\x7F")n.name="delete"',
|
|
19
|
+
patched: 'else if(e==="\\x7F"||e==="\\x1B\\x7F")n.name="backspace"',
|
|
20
|
+
description: 'Map \\x7F to backspace instead of delete'
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
claude: {
|
|
25
|
+
name: 'Claude Code CLI',
|
|
26
|
+
package: '@anthropic-ai/claude-code',
|
|
27
|
+
file: 'cli.mjs',
|
|
28
|
+
patterns: [
|
|
29
|
+
{
|
|
30
|
+
original: 'else if(e==="\\x7F"||e==="\\x1B\\x7F")n.name="delete"',
|
|
31
|
+
patched: 'else if(e==="\\x7F"||e==="\\x1B\\x7F")n.name="backspace"',
|
|
32
|
+
description: 'Map \\x7F to backspace instead of delete'
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get npm global root path
|
|
40
|
+
*/
|
|
41
|
+
function getNpmGlobalRoot() {
|
|
42
|
+
try {
|
|
43
|
+
return execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
44
|
+
} catch (e) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Find installation path for a package
|
|
51
|
+
*/
|
|
52
|
+
function findPackagePath(packageName, fileName) {
|
|
53
|
+
const possibleRoots = [
|
|
54
|
+
getNpmGlobalRoot(),
|
|
55
|
+
path.join(process.env.APPDATA || '', 'npm', 'node_modules'),
|
|
56
|
+
path.join(process.env.NVM_HOME || '', 'nodejs', 'node_modules'),
|
|
57
|
+
'C:\\nvm4w\\nodejs\\node_modules',
|
|
58
|
+
'/usr/local/lib/node_modules',
|
|
59
|
+
'/usr/lib/node_modules',
|
|
60
|
+
path.join(process.env.HOME || '', '.nvm', 'versions', 'node', process.version, 'lib', 'node_modules'),
|
|
61
|
+
].filter(Boolean);
|
|
62
|
+
|
|
63
|
+
for (const root of possibleRoots) {
|
|
64
|
+
const filePath = path.join(root, packageName, fileName);
|
|
65
|
+
if (fs.existsSync(filePath)) {
|
|
66
|
+
return filePath;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check patch status for a target
|
|
74
|
+
*/
|
|
75
|
+
function checkPatchStatus(targetKey) {
|
|
76
|
+
const config = PATCH_CONFIGS[targetKey];
|
|
77
|
+
if (!config) {
|
|
78
|
+
return { found: false, error: `Unknown target: ${targetKey}` };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const filePath = findPackagePath(config.package, config.file);
|
|
82
|
+
if (!filePath) {
|
|
83
|
+
return { found: false, installed: false, name: config.name };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
87
|
+
|
|
88
|
+
for (const pattern of config.patterns) {
|
|
89
|
+
if (content.includes(pattern.patched)) {
|
|
90
|
+
return { found: true, installed: true, patched: true, path: filePath, name: config.name };
|
|
91
|
+
}
|
|
92
|
+
if (content.includes(pattern.original)) {
|
|
93
|
+
return { found: true, installed: true, patched: false, path: filePath, name: config.name };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { found: true, installed: true, patched: 'unknown', path: filePath, name: config.name };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Apply patch to a target
|
|
102
|
+
*/
|
|
103
|
+
function applyPatch(targetKey) {
|
|
104
|
+
const config = PATCH_CONFIGS[targetKey];
|
|
105
|
+
const status = checkPatchStatus(targetKey);
|
|
106
|
+
|
|
107
|
+
if (!status.installed) {
|
|
108
|
+
console.log(`⚠️ ${config.name}: Not installed`);
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (status.patched === true) {
|
|
113
|
+
console.log(`✅ ${config.name}: Already patched`);
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let content = fs.readFileSync(status.path, 'utf8');
|
|
118
|
+
|
|
119
|
+
// Create backup
|
|
120
|
+
const backupPath = status.path + '.backup';
|
|
121
|
+
if (!fs.existsSync(backupPath)) {
|
|
122
|
+
fs.writeFileSync(backupPath, content);
|
|
123
|
+
console.log(`📦 Created backup: ${backupPath}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Apply patches
|
|
127
|
+
let patchedCount = 0;
|
|
128
|
+
for (const pattern of config.patterns) {
|
|
129
|
+
if (content.includes(pattern.original)) {
|
|
130
|
+
content = content.replace(pattern.original, pattern.patched);
|
|
131
|
+
patchedCount++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (patchedCount > 0) {
|
|
136
|
+
fs.writeFileSync(status.path, content);
|
|
137
|
+
console.log(`✅ ${config.name}: Patched successfully (${patchedCount} changes)`);
|
|
138
|
+
return true;
|
|
139
|
+
} else {
|
|
140
|
+
console.log(`⚠️ ${config.name}: Could not find patterns to patch`);
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Restore original file from backup
|
|
147
|
+
*/
|
|
148
|
+
function restoreBackup(targetKey) {
|
|
149
|
+
const config = PATCH_CONFIGS[targetKey];
|
|
150
|
+
const status = checkPatchStatus(targetKey);
|
|
151
|
+
|
|
152
|
+
if (!status.installed) {
|
|
153
|
+
console.log(`⚠️ ${config.name}: Not installed`);
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const backupPath = status.path + '.backup';
|
|
158
|
+
if (fs.existsSync(backupPath)) {
|
|
159
|
+
fs.copyFileSync(backupPath, status.path);
|
|
160
|
+
console.log(`✅ ${config.name}: Restored from backup`);
|
|
161
|
+
return true;
|
|
162
|
+
} else {
|
|
163
|
+
console.log(`⚠️ ${config.name}: No backup found`);
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Export functions
|
|
169
|
+
module.exports = {
|
|
170
|
+
async patch(target = 'all') {
|
|
171
|
+
const targets = target === 'all' ? Object.keys(PATCH_CONFIGS) : [target];
|
|
172
|
+
for (const t of targets) {
|
|
173
|
+
applyPatch(t);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
async check(target = 'all') {
|
|
178
|
+
const targets = target === 'all' ? Object.keys(PATCH_CONFIGS) : [target];
|
|
179
|
+
for (const t of targets) {
|
|
180
|
+
const status = checkPatchStatus(t);
|
|
181
|
+
if (!status.installed) {
|
|
182
|
+
console.log(`⬚ ${status.name}: Not installed`);
|
|
183
|
+
} else if (status.patched === true) {
|
|
184
|
+
console.log(`✅ ${status.name}: Patched`);
|
|
185
|
+
} else if (status.patched === false) {
|
|
186
|
+
console.log(`❌ ${status.name}: Not patched`);
|
|
187
|
+
} else {
|
|
188
|
+
console.log(`⚠️ ${status.name}: Unknown status`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
async restore(target = 'all') {
|
|
194
|
+
const targets = target === 'all' ? Object.keys(PATCH_CONFIGS) : [target];
|
|
195
|
+
for (const t of targets) {
|
|
196
|
+
restoreBackup(t);
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
async findTargets() {
|
|
201
|
+
for (const [key, config] of Object.entries(PATCH_CONFIGS)) {
|
|
202
|
+
const status = checkPatchStatus(key);
|
|
203
|
+
const statusIcon = !status.installed ? '⬚' : status.patched ? '✅' : '❌';
|
|
204
|
+
const statusText = !status.installed ? 'not installed' : status.patched ? 'patched' : 'not patched';
|
|
205
|
+
console.log(` ${statusIcon} ${key.padEnd(10)} ${config.name} (${statusText})`);
|
|
206
|
+
if (status.path) {
|
|
207
|
+
console.log(` Path: ${status.path}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
PATCH_CONFIGS,
|
|
213
|
+
checkPatchStatus,
|
|
214
|
+
applyPatch,
|
|
215
|
+
restoreBackup,
|
|
216
|
+
findPackagePath
|
|
217
|
+
};
|
|
218
|
+
|