english-optimizer-cli 1.5.0 → 1.6.1
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/.claude/settings.local.json +12 -0
- package/.prettierrc.json +10 -0
- package/API_SETUP.md +2 -2
- package/LICENSE +21 -0
- package/README.md +13 -89
- package/README.zh-CN.md +458 -0
- package/SECURITY.md +53 -0
- package/dist/ai/api-provider.js +41 -76
- package/dist/ai/ollama.js +23 -45
- package/dist/config/config.js +0 -3
- package/dist/config/test.js +5 -5
- package/dist/config/wizard.js +1 -1
- package/dist/core/instant-editor.js +1 -1
- package/dist/core/optimizer.js +13 -31
- package/dist/index.js +9 -8
- package/dist/prompts/templates.js +0 -4
- package/dist/prompts/translation-prompt.js +4 -6
- package/dist/utils/display.js +14 -16
- package/dist/utils/display.test.js +10 -0
- package/eslint.config.mjs +37 -0
- package/package.json +31 -19
package/SECURITY.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
We release patches for security vulnerabilities for the following versions:
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
8
|
+
| ------- | ------------------ |
|
|
9
|
+
| 1.5.x | :white_check_mark: |
|
|
10
|
+
| < 1.5 | :x: |
|
|
11
|
+
|
|
12
|
+
## Reporting a Vulnerability
|
|
13
|
+
|
|
14
|
+
We take the security of English Optimizer CLI seriously. If you believe you have found a security vulnerability, please report it to us as described below.
|
|
15
|
+
|
|
16
|
+
**Please do not report security vulnerabilities through public GitHub issues.**
|
|
17
|
+
|
|
18
|
+
Instead, please report them via email to:
|
|
19
|
+
- Create a private security advisory on GitHub: https://github.com/[your-username]/english-optimizer-cli/security/advisories/new
|
|
20
|
+
|
|
21
|
+
Please include the following information:
|
|
22
|
+
- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
|
23
|
+
- Full paths of source file(s) related to the manifestation of the issue
|
|
24
|
+
- The location of the affected source code (tag/branch/commit or direct URL)
|
|
25
|
+
- Any special configuration required to reproduce the issue
|
|
26
|
+
- Step-by-step instructions to reproduce the issue
|
|
27
|
+
- Proof-of-concept or exploit code (if possible)
|
|
28
|
+
- Impact of the issue, including how an attacker might exploit it
|
|
29
|
+
|
|
30
|
+
## What to Expect
|
|
31
|
+
|
|
32
|
+
- You will receive a response from us within 48 hours
|
|
33
|
+
- We will work with you to understand and validate the issue
|
|
34
|
+
- We will develop and release a fix as quickly as possible
|
|
35
|
+
- We will publicly acknowledge your responsible disclosure, if you wish
|
|
36
|
+
|
|
37
|
+
## Security Best Practices for Users
|
|
38
|
+
|
|
39
|
+
When using English Optimizer CLI:
|
|
40
|
+
|
|
41
|
+
1. **API Keys**: Never commit API keys to version control. Use `.env` files (which are gitignored by default)
|
|
42
|
+
2. **Updates**: Keep the CLI updated to the latest version to get security patches
|
|
43
|
+
3. **Permissions**: Be cautious when granting file system permissions
|
|
44
|
+
4. **Network**: If using cloud APIs, ensure you're using HTTPS endpoints
|
|
45
|
+
5. **Dependencies**: Regularly check for and update dependencies with known vulnerabilities
|
|
46
|
+
|
|
47
|
+
## Known Security Considerations
|
|
48
|
+
|
|
49
|
+
- API keys are stored in plain text in configuration files (`~/.english-optimizer/config.yaml` or `.env`)
|
|
50
|
+
- Ensure proper file permissions on configuration files containing API keys
|
|
51
|
+
- Be cautious when sharing history files as they may contain sensitive text
|
|
52
|
+
|
|
53
|
+
Thank you for helping keep English Optimizer CLI and its users safe!
|
package/dist/ai/api-provider.js
CHANGED
|
@@ -11,66 +11,37 @@ class ApiProvider {
|
|
|
11
11
|
constructor(config) {
|
|
12
12
|
this.config = config;
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
temperature: 0.7,
|
|
26
|
-
max_tokens: 2000,
|
|
27
|
-
}, {
|
|
28
|
-
headers: {
|
|
29
|
-
Authorization: `Bearer ${this.config.apiKey}`,
|
|
30
|
-
'Content-Type': 'application/json',
|
|
31
|
-
},
|
|
32
|
-
timeout: 60000, // 60 seconds timeout
|
|
33
|
-
});
|
|
34
|
-
if (response.data && response.data.choices && response.data.choices.length > 0) {
|
|
35
|
-
let result = response.data.choices[0].message.content.trim();
|
|
36
|
-
// Remove quotes if model wrapped the response in them
|
|
37
|
-
if (result.startsWith('"') && result.endsWith('"')) {
|
|
38
|
-
result = result.slice(1, -1);
|
|
14
|
+
handleApiError(error) {
|
|
15
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
16
|
+
const axiosError = error;
|
|
17
|
+
if (axiosError.response) {
|
|
18
|
+
const status = axiosError.response.status;
|
|
19
|
+
const data = axiosError.response.data;
|
|
20
|
+
// Handle GLM specific errors
|
|
21
|
+
if (data?.error?.code === '1113') {
|
|
22
|
+
throw new Error('GLM account balance is insufficient. Please recharge your account at https://open.bigmodel.cn/usercenter/finance');
|
|
39
23
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
for (const prefix of prefixesToRemove) {
|
|
43
|
-
if (result.startsWith(prefix)) {
|
|
44
|
-
result = result.slice(prefix.length).trim();
|
|
45
|
-
}
|
|
24
|
+
if (status === 401) {
|
|
25
|
+
throw new Error('Invalid API key. Please check your API credentials.');
|
|
46
26
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
throw new Error('Invalid response from API');
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
if (axios_1.default.isAxiosError(error)) {
|
|
53
|
-
const axiosError = error;
|
|
54
|
-
if (axiosError.response) {
|
|
55
|
-
const status = axiosError.response.status;
|
|
56
|
-
const data = axiosError.response.data;
|
|
57
|
-
// Handle GLM specific errors
|
|
58
|
-
if (data?.error?.code === '1113') {
|
|
59
|
-
throw new Error('GLM account balance is insufficient. Please recharge your account at https://open.bigmodel.cn/usercenter/finance');
|
|
60
|
-
}
|
|
61
|
-
if (status === 401) {
|
|
62
|
-
throw new Error('Invalid API key. Please check your API credentials.');
|
|
63
|
-
}
|
|
64
|
-
else if (status === 429) {
|
|
65
|
-
throw new Error('Rate limit exceeded. Please try again later.');
|
|
66
|
-
}
|
|
27
|
+
else if (status === 429) {
|
|
28
|
+
throw new Error('Rate limit exceeded. Please try again later.');
|
|
67
29
|
}
|
|
68
|
-
throw new Error(`API error: ${axiosError.message}`);
|
|
69
30
|
}
|
|
70
|
-
throw error;
|
|
31
|
+
throw new Error(`API error: ${axiosError.message}`);
|
|
71
32
|
}
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
async optimize(text, mode) {
|
|
36
|
+
const prompt = (0, templates_1.getPromptTemplate)(mode, text);
|
|
37
|
+
const result = await this.callAPI(prompt);
|
|
38
|
+
return this.cleanupResponse(result, true);
|
|
72
39
|
}
|
|
73
40
|
async generateWithPrompt(prompt) {
|
|
41
|
+
const result = await this.callAPI(prompt);
|
|
42
|
+
return this.cleanupResponse(result, false);
|
|
43
|
+
}
|
|
44
|
+
async callAPI(prompt) {
|
|
74
45
|
try {
|
|
75
46
|
const response = await axios_1.default.post(`${this.config.baseUrl}/chat/completions`, {
|
|
76
47
|
model: this.config.model,
|
|
@@ -90,36 +61,30 @@ class ApiProvider {
|
|
|
90
61
|
timeout: 60000,
|
|
91
62
|
});
|
|
92
63
|
if (response.data && response.data.choices && response.data.choices.length > 0) {
|
|
93
|
-
|
|
94
|
-
// Remove quotes if model wrapped the response in them
|
|
95
|
-
if (result.startsWith('"') && result.endsWith('"')) {
|
|
96
|
-
result = result.slice(1, -1);
|
|
97
|
-
}
|
|
98
|
-
return result;
|
|
64
|
+
return response.data.choices[0].message.content.trim();
|
|
99
65
|
}
|
|
100
66
|
throw new Error('Invalid response from API');
|
|
101
67
|
}
|
|
102
68
|
catch (error) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
69
|
+
this.handleApiError(error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
cleanupResponse(result, removePrefixes) {
|
|
73
|
+
// Remove quotes if model wrapped the response in them
|
|
74
|
+
if (result.startsWith('"') && result.endsWith('"')) {
|
|
75
|
+
result = result.slice(1, -1);
|
|
76
|
+
}
|
|
77
|
+
// Remove common prefixes if present
|
|
78
|
+
if (removePrefixes) {
|
|
79
|
+
const prefixesToRemove = ['Rewritten text:', 'Corrected text:', 'Optimized text:'];
|
|
80
|
+
for (const prefix of prefixesToRemove) {
|
|
81
|
+
if (result.startsWith(prefix)) {
|
|
82
|
+
result = result.slice(prefix.length).trim();
|
|
83
|
+
break;
|
|
118
84
|
}
|
|
119
|
-
throw new Error(`API error: ${axiosError.message}`);
|
|
120
85
|
}
|
|
121
|
-
throw error;
|
|
122
86
|
}
|
|
87
|
+
return result;
|
|
123
88
|
}
|
|
124
89
|
async isAvailable() {
|
|
125
90
|
if (!this.config.apiKey) {
|
package/dist/ai/ollama.js
CHANGED
|
@@ -12,33 +12,28 @@ class OllamaProvider {
|
|
|
12
12
|
}
|
|
13
13
|
async optimize(text, mode) {
|
|
14
14
|
const prompt = (0, templates_1.getPromptTemplate)(mode, text);
|
|
15
|
+
const result = await this.callOllama(prompt);
|
|
16
|
+
return this.cleanupResponse(result, true);
|
|
17
|
+
}
|
|
18
|
+
async generateWithPrompt(prompt) {
|
|
19
|
+
const result = await this.callOllama(prompt);
|
|
20
|
+
return this.cleanupResponse(result, false);
|
|
21
|
+
}
|
|
22
|
+
async callOllama(prompt) {
|
|
15
23
|
try {
|
|
16
24
|
const response = await axios_1.default.post(`${this.config.baseUrl}/api/generate`, {
|
|
17
25
|
model: this.config.model,
|
|
18
|
-
prompt
|
|
26
|
+
prompt,
|
|
19
27
|
stream: false,
|
|
20
28
|
options: {
|
|
21
29
|
temperature: 0.7,
|
|
22
30
|
top_p: 0.9,
|
|
23
31
|
},
|
|
24
32
|
}, {
|
|
25
|
-
timeout: 60000,
|
|
33
|
+
timeout: 60000,
|
|
26
34
|
});
|
|
27
35
|
if (response.data && response.data.response) {
|
|
28
|
-
|
|
29
|
-
let result = response.data.response.trim();
|
|
30
|
-
// Remove quotes if the model wrapped the response in them
|
|
31
|
-
if (result.startsWith('"') && result.endsWith('"')) {
|
|
32
|
-
result = result.slice(1, -1);
|
|
33
|
-
}
|
|
34
|
-
// Remove common prefixes if present
|
|
35
|
-
const prefixesToRemove = ['Rewritten text:', 'Corrected text:', 'Optimized text:'];
|
|
36
|
-
for (const prefix of prefixesToRemove) {
|
|
37
|
-
if (result.startsWith(prefix)) {
|
|
38
|
-
result = result.slice(prefix.length).trim();
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return result;
|
|
36
|
+
return response.data.response.trim();
|
|
42
37
|
}
|
|
43
38
|
throw new Error('Invalid response from Ollama');
|
|
44
39
|
}
|
|
@@ -53,39 +48,22 @@ class OllamaProvider {
|
|
|
53
48
|
throw error;
|
|
54
49
|
}
|
|
55
50
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
prompt: prompt,
|
|
61
|
-
stream: false,
|
|
62
|
-
options: {
|
|
63
|
-
temperature: 0.7,
|
|
64
|
-
top_p: 0.9,
|
|
65
|
-
},
|
|
66
|
-
}, {
|
|
67
|
-
timeout: 60000,
|
|
68
|
-
});
|
|
69
|
-
if (response.data && response.data.response) {
|
|
70
|
-
let result = response.data.response.trim();
|
|
71
|
-
// Remove quotes if the model wrapped the response in them
|
|
72
|
-
if (result.startsWith('"') && result.endsWith('"')) {
|
|
73
|
-
result = result.slice(1, -1);
|
|
74
|
-
}
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
throw new Error('Invalid response from Ollama');
|
|
51
|
+
cleanupResponse(result, removePrefixes) {
|
|
52
|
+
// Remove quotes if the model wrapped the response in them
|
|
53
|
+
if (result.startsWith('"') && result.endsWith('"')) {
|
|
54
|
+
result = result.slice(1, -1);
|
|
78
55
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
56
|
+
// Remove common prefixes if present
|
|
57
|
+
if (removePrefixes) {
|
|
58
|
+
const prefixesToRemove = ['Rewritten text:', 'Corrected text:', 'Optimized text:'];
|
|
59
|
+
for (const prefix of prefixesToRemove) {
|
|
60
|
+
if (result.startsWith(prefix)) {
|
|
61
|
+
result = result.slice(prefix.length).trim();
|
|
62
|
+
break;
|
|
84
63
|
}
|
|
85
|
-
throw new Error(`Ollama API error: ${axiosError.message}`);
|
|
86
64
|
}
|
|
87
|
-
throw error;
|
|
88
65
|
}
|
|
66
|
+
return result;
|
|
89
67
|
}
|
|
90
68
|
async isAvailable() {
|
|
91
69
|
try {
|
package/dist/config/config.js
CHANGED
|
@@ -111,10 +111,8 @@ class ConfigManager {
|
|
|
111
111
|
}
|
|
112
112
|
loadConfig() {
|
|
113
113
|
// Try to load from YAML config file first, then JSON
|
|
114
|
-
let configFilePath = null;
|
|
115
114
|
let fileContent = '';
|
|
116
115
|
if ((0, fs_1.existsSync)(CONFIG_FILE_YAML)) {
|
|
117
|
-
configFilePath = CONFIG_FILE_YAML;
|
|
118
116
|
try {
|
|
119
117
|
fileContent = (0, fs_1.readFileSync)(CONFIG_FILE_YAML, 'utf-8');
|
|
120
118
|
const fileConfig = yaml.load(fileContent);
|
|
@@ -127,7 +125,6 @@ class ConfigManager {
|
|
|
127
125
|
}
|
|
128
126
|
}
|
|
129
127
|
if ((0, fs_1.existsSync)(CONFIG_FILE_JSON)) {
|
|
130
|
-
configFilePath = CONFIG_FILE_JSON;
|
|
131
128
|
try {
|
|
132
129
|
fileContent = (0, fs_1.readFileSync)(CONFIG_FILE_JSON, 'utf-8');
|
|
133
130
|
const fileConfig = JSON.parse(fileContent);
|
package/dist/config/test.js
CHANGED
|
@@ -36,7 +36,7 @@ async function testConfiguration() {
|
|
|
36
36
|
console.log(chalk_1.default.cyan('💡 To fix:'));
|
|
37
37
|
console.log(chalk_1.default.white(' 1. Visit https://open.bigmodel.cn/usercenter/finance'));
|
|
38
38
|
console.log(chalk_1.default.white(' 2. Recharge your account'));
|
|
39
|
-
console.log(chalk_1.default.white(' 3. Run test again:
|
|
39
|
+
console.log(chalk_1.default.white(' 3. Run test again: cao test\n'));
|
|
40
40
|
process.exit(1);
|
|
41
41
|
}
|
|
42
42
|
throw e;
|
|
@@ -60,13 +60,13 @@ async function testConfiguration() {
|
|
|
60
60
|
console.log(chalk_1.default.gray(' - Model may not be available'));
|
|
61
61
|
console.log(chalk_1.default.gray(' - API endpoint may have changed'));
|
|
62
62
|
console.log('\n' + chalk_1.default.cyan('Please check your API configuration:'));
|
|
63
|
-
console.log(chalk_1.default.white('
|
|
63
|
+
console.log(chalk_1.default.white(' cao config --setup\n'));
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
else {
|
|
67
67
|
console.log(chalk_1.default.green.bold('🎉 Your local Ollama is ready!\n'));
|
|
68
68
|
console.log(chalk_1.default.gray('You can start using:'));
|
|
69
|
-
console.log(chalk_1.default.white.bold('
|
|
69
|
+
console.log(chalk_1.default.white.bold(' cao\n'));
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
else {
|
|
@@ -86,9 +86,9 @@ async function testConfiguration() {
|
|
|
86
86
|
console.log(chalk_1.default.gray(' - API URL is incorrect'));
|
|
87
87
|
console.log(chalk_1.default.gray(' - Network connection failed'));
|
|
88
88
|
console.log('\n' + chalk_1.default.cyan('Please check your configuration:'));
|
|
89
|
-
console.log(chalk_1.default.white('
|
|
89
|
+
console.log(chalk_1.default.white(' cao config --setup'));
|
|
90
90
|
console.log(chalk_1.default.gray('\nOr view current config:'));
|
|
91
|
-
console.log(chalk_1.default.white('
|
|
91
|
+
console.log(chalk_1.default.white(' cao config\n'));
|
|
92
92
|
}
|
|
93
93
|
process.exit(1);
|
|
94
94
|
}
|
package/dist/config/wizard.js
CHANGED
|
@@ -178,6 +178,6 @@ async function runConfigWizard() {
|
|
|
178
178
|
console.log(chalk_1.default.cyan(`YAML Prompt: ${answers.enableYAMLPrompt ? 'Enabled' : 'Disabled'}`));
|
|
179
179
|
console.log(chalk_1.default.gray('─'.repeat(50)));
|
|
180
180
|
console.log(chalk_1.default.green('\n🎉 Ready to use! Run:'));
|
|
181
|
-
console.log(chalk_1.default.white.bold('
|
|
181
|
+
console.log(chalk_1.default.white.bold(' cao\n'));
|
|
182
182
|
}
|
|
183
183
|
//# sourceMappingURL=wizard.js.map
|
|
@@ -109,7 +109,7 @@ class InstantEditor {
|
|
|
109
109
|
clipboardy_1.default.writeSync(optimized);
|
|
110
110
|
console.log(chalk_1.default.green.bold('\n✓ 已复制到剪贴板 / Copied to clipboard!\n'));
|
|
111
111
|
}
|
|
112
|
-
catch
|
|
112
|
+
catch {
|
|
113
113
|
console.log(chalk_1.default.yellow('\n⚠️ 剪贴板复制失败 / Failed to copy to clipboard\n'));
|
|
114
114
|
}
|
|
115
115
|
// Save to history
|
package/dist/core/optimizer.js
CHANGED
|
@@ -12,22 +12,8 @@ class Optimizer {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
async optimize(text, mode) {
|
|
15
|
-
const startTime = Date.now();
|
|
16
15
|
const optimized = await this.provider.optimize(text, mode);
|
|
17
|
-
|
|
18
|
-
const result = {
|
|
19
|
-
original: text,
|
|
20
|
-
optimized,
|
|
21
|
-
mode,
|
|
22
|
-
timestamp: new Date(),
|
|
23
|
-
provider: this.getProviderName(),
|
|
24
|
-
model: this.getModelName(),
|
|
25
|
-
};
|
|
26
|
-
// Save to history if enabled
|
|
27
|
-
if (this.historyLogger) {
|
|
28
|
-
this.historyLogger.addEntry(result);
|
|
29
|
-
}
|
|
30
|
-
return result;
|
|
16
|
+
return this.createResult(text, optimized, mode);
|
|
31
17
|
}
|
|
32
18
|
async optimizeWithYAMLPrompt(text) {
|
|
33
19
|
if (!this.yamlPromptLoader || !this.yamlPromptLoader.hasYAMLPrompt()) {
|
|
@@ -35,10 +21,20 @@ class Optimizer {
|
|
|
35
21
|
}
|
|
36
22
|
const prompt = this.yamlPromptLoader.buildPrompt(text);
|
|
37
23
|
const optimized = await this.provider.generateWithPrompt(prompt);
|
|
24
|
+
return this.createResult(text, optimized, types_1.OptimizationMode.PROFESSIONAL);
|
|
25
|
+
}
|
|
26
|
+
hasYAMLPrompt() {
|
|
27
|
+
return this.yamlPromptLoader?.hasYAMLPrompt() || false;
|
|
28
|
+
}
|
|
29
|
+
async optimizeWithCustomPrompt(text, _customPrompt) {
|
|
30
|
+
const optimized = await this.provider.optimize(text, types_1.OptimizationMode.PROFESSIONAL);
|
|
31
|
+
return this.createResult(text, optimized, types_1.OptimizationMode.PROFESSIONAL);
|
|
32
|
+
}
|
|
33
|
+
createResult(original, optimized, mode) {
|
|
38
34
|
const result = {
|
|
39
|
-
original
|
|
35
|
+
original,
|
|
40
36
|
optimized,
|
|
41
|
-
mode
|
|
37
|
+
mode,
|
|
42
38
|
timestamp: new Date(),
|
|
43
39
|
provider: this.getProviderName(),
|
|
44
40
|
model: this.getModelName(),
|
|
@@ -49,20 +45,6 @@ class Optimizer {
|
|
|
49
45
|
}
|
|
50
46
|
return result;
|
|
51
47
|
}
|
|
52
|
-
hasYAMLPrompt() {
|
|
53
|
-
return this.yamlPromptLoader?.hasYAMLPrompt() || false;
|
|
54
|
-
}
|
|
55
|
-
async optimizeWithCustomPrompt(text, customPrompt) {
|
|
56
|
-
const optimized = await this.provider.optimize(text, types_1.OptimizationMode.PROFESSIONAL);
|
|
57
|
-
return {
|
|
58
|
-
original: text,
|
|
59
|
-
optimized,
|
|
60
|
-
mode: types_1.OptimizationMode.PROFESSIONAL,
|
|
61
|
-
timestamp: new Date(),
|
|
62
|
-
provider: this.getProviderName(),
|
|
63
|
-
model: this.getModelName(),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
48
|
getProviderName() {
|
|
67
49
|
return this.provider.constructor.name;
|
|
68
50
|
}
|
package/dist/index.js
CHANGED
|
@@ -40,6 +40,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
40
40
|
const commander_1 = require("commander");
|
|
41
41
|
const chalk_1 = __importDefault(require("chalk"));
|
|
42
42
|
const axios_1 = __importDefault(require("axios"));
|
|
43
|
+
const child_process_1 = require("child_process");
|
|
43
44
|
const config_1 = require("./config/config");
|
|
44
45
|
const provider_1 = require("./ai/provider");
|
|
45
46
|
const optimizer_1 = require("./core/optimizer");
|
|
@@ -49,11 +50,12 @@ const batch_1 = require("./core/batch");
|
|
|
49
50
|
const logger_1 = require("./history/logger");
|
|
50
51
|
const custom_1 = require("./prompts/custom");
|
|
51
52
|
const display_1 = require("./utils/display");
|
|
53
|
+
const package_json_1 = __importDefault(require("../package.json"));
|
|
52
54
|
const PACKAGE_NAME = 'english-optimizer-cli';
|
|
53
|
-
const PACKAGE_VERSION =
|
|
55
|
+
const PACKAGE_VERSION = package_json_1.default.version;
|
|
54
56
|
const program = new commander_1.Command();
|
|
55
57
|
program
|
|
56
|
-
.name('
|
|
58
|
+
.name('cao')
|
|
57
59
|
.description('CLI tool to help non-native English speakers improve their writing using AI')
|
|
58
60
|
.version(PACKAGE_VERSION);
|
|
59
61
|
program
|
|
@@ -210,13 +212,12 @@ program
|
|
|
210
212
|
}
|
|
211
213
|
}
|
|
212
214
|
else if (options.edit) {
|
|
213
|
-
const { execSync } = require('child_process');
|
|
214
215
|
const promptPath = hasYAMLPrompt
|
|
215
216
|
? yamlPromptLoader.getPromptPath()
|
|
216
217
|
: textPromptLoader.getPromptPath();
|
|
217
218
|
console.log(`\n📝 Opening ${promptPath} in your default editor...\n`);
|
|
218
219
|
try {
|
|
219
|
-
execSync(`"${process.env.EDITOR || 'code'}" "${promptPath}"`, { stdio: 'inherit' });
|
|
220
|
+
(0, child_process_1.execSync)(`"${process.env.EDITOR || 'code'}" "${promptPath}"`, { stdio: 'inherit' });
|
|
220
221
|
}
|
|
221
222
|
catch {
|
|
222
223
|
console.log(chalk_1.default.yellow('\n⚠️ Could not open editor. Please edit manually:'));
|
|
@@ -232,8 +233,8 @@ program
|
|
|
232
233
|
console.log(`\n📄 Prompt file location: ${textPromptLoader.getPromptPath()}`);
|
|
233
234
|
}
|
|
234
235
|
console.log(chalk_1.default.gray('\nCommands:'));
|
|
235
|
-
console.log('
|
|
236
|
-
console.log('
|
|
236
|
+
console.log(' cao prompt --show Show current prompt');
|
|
237
|
+
console.log(' cao prompt --edit Edit prompt file');
|
|
237
238
|
console.log(chalk_1.default.gray('\nEdit the file directly to customize translation behavior.\n'));
|
|
238
239
|
}
|
|
239
240
|
}
|
|
@@ -292,7 +293,7 @@ program
|
|
|
292
293
|
try {
|
|
293
294
|
console.log(chalk_1.default.cyan('\n🔄 Checking for updates...\n'));
|
|
294
295
|
// Get current version
|
|
295
|
-
const
|
|
296
|
+
const currentVersion = package_json_1.default.version;
|
|
296
297
|
// Fetch latest version from npm
|
|
297
298
|
try {
|
|
298
299
|
const response = await axios_1.default.get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`);
|
|
@@ -310,7 +311,7 @@ program
|
|
|
310
311
|
console.log(chalk_1.default.gray(' npm install -g english-optimizer-cli@latest\n'));
|
|
311
312
|
}
|
|
312
313
|
}
|
|
313
|
-
catch
|
|
314
|
+
catch {
|
|
314
315
|
console.log(chalk_1.default.yellow('⚠️ Could not check for updates.\n'));
|
|
315
316
|
console.log(chalk_1.default.gray('Please check manually:'));
|
|
316
317
|
console.log(chalk_1.default.gray(' npm view english-optimizer-cli version\n'));
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PROMPT_TEMPLATES = void 0;
|
|
4
4
|
exports.getPromptTemplate = getPromptTemplate;
|
|
5
|
-
exports.getAllModes = getAllModes;
|
|
6
5
|
exports.getModeInfo = getModeInfo;
|
|
7
6
|
const types_1 = require("../types");
|
|
8
7
|
exports.PROMPT_TEMPLATES = {
|
|
@@ -50,9 +49,6 @@ Rewritten text:`,
|
|
|
50
49
|
function getPromptTemplate(mode, text) {
|
|
51
50
|
return exports.PROMPT_TEMPLATES[mode].template(text);
|
|
52
51
|
}
|
|
53
|
-
function getAllModes() {
|
|
54
|
-
return Object.values(types_1.OptimizationMode);
|
|
55
|
-
}
|
|
56
52
|
function getModeInfo(mode) {
|
|
57
53
|
return exports.PROMPT_TEMPLATES[mode];
|
|
58
54
|
}
|
|
@@ -41,13 +41,11 @@ Provide ONLY the translated/optimized English text, nothing else.
|
|
|
41
41
|
Do not include any explanations, notes, or additional text.`;
|
|
42
42
|
}
|
|
43
43
|
createDefaultPromptFile() {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (!require('fs').existsSync(dir)) {
|
|
48
|
-
mkdirSync(dir, { recursive: true });
|
|
44
|
+
const dir = (0, path_1.dirname)(this.promptPath);
|
|
45
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
46
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
49
47
|
}
|
|
50
|
-
writeFileSync(this.promptPath, this.getDefaultPrompt(), 'utf-8');
|
|
48
|
+
(0, fs_1.writeFileSync)(this.promptPath, this.getDefaultPrompt(), 'utf-8');
|
|
51
49
|
}
|
|
52
50
|
getPromptPath() {
|
|
53
51
|
return this.promptPath;
|
package/dist/utils/display.js
CHANGED
|
@@ -10,7 +10,6 @@ exports.displayDiff = displayDiff;
|
|
|
10
10
|
exports.displayError = displayError;
|
|
11
11
|
exports.displaySuccess = displaySuccess;
|
|
12
12
|
exports.displayInfo = displayInfo;
|
|
13
|
-
exports.displayWarning = displayWarning;
|
|
14
13
|
exports.displayHelp = displayHelp;
|
|
15
14
|
exports.displayHistoryEntry = displayHistoryEntry;
|
|
16
15
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -25,14 +24,9 @@ function displayModeInfo(mode) {
|
|
|
25
24
|
console.log(chalk_1.default.gray(modeInfo.description));
|
|
26
25
|
}
|
|
27
26
|
function displayOptimization(original, optimized) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
console.log(chalk_1.default.gray(original));
|
|
32
|
-
// Show optimized
|
|
33
|
-
console.log(chalk_1.default.green('\n✅ Optimized:'));
|
|
34
|
-
console.log(chalk_1.default.white(optimized));
|
|
35
|
-
console.log(chalk_1.default.gray('\n' + '─'.repeat(60)));
|
|
27
|
+
displaySeparator();
|
|
28
|
+
displayOriginalAndOptimized(original, optimized);
|
|
29
|
+
displaySeparator();
|
|
36
30
|
}
|
|
37
31
|
function displayDiff(original, optimized) {
|
|
38
32
|
const differences = (0, diff_1.diffLines)(original, optimized);
|
|
@@ -53,9 +47,6 @@ function displaySuccess(message) {
|
|
|
53
47
|
function displayInfo(message) {
|
|
54
48
|
console.log(chalk_1.default.cyan('\nℹ️'), chalk_1.default.white(message));
|
|
55
49
|
}
|
|
56
|
-
function displayWarning(message) {
|
|
57
|
-
console.log(chalk_1.default.yellow('\n⚠️'), chalk_1.default.white(message));
|
|
58
|
-
}
|
|
59
50
|
function displayHelp() {
|
|
60
51
|
console.log(chalk_1.default.cyan('\n📖 Hotkeys:\n'));
|
|
61
52
|
console.log(chalk_1.default.gray(' Ctrl+P - Professional tone'));
|
|
@@ -67,14 +58,21 @@ function displayHelp() {
|
|
|
67
58
|
console.log(chalk_1.default.gray(' Ctrl+Q - Quit\n'));
|
|
68
59
|
}
|
|
69
60
|
function displayHistoryEntry(entry) {
|
|
70
|
-
|
|
61
|
+
displaySeparator();
|
|
71
62
|
console.log(chalk_1.default.cyan(`\nID: ${entry.id}`));
|
|
72
63
|
console.log(chalk_1.default.gray(`Time: ${entry.timestamp.toLocaleString()}`));
|
|
73
64
|
console.log(chalk_1.default.yellow(`Mode: ${(0, templates_1.getModeInfo)(entry.mode).name}`));
|
|
65
|
+
displayOriginalAndOptimized(entry.original, entry.optimized);
|
|
66
|
+
displaySeparator();
|
|
67
|
+
}
|
|
68
|
+
// Helper functions
|
|
69
|
+
function displaySeparator() {
|
|
70
|
+
console.log(chalk_1.default.gray('\n' + '─'.repeat(60)));
|
|
71
|
+
}
|
|
72
|
+
function displayOriginalAndOptimized(original, optimized) {
|
|
74
73
|
console.log(chalk_1.default.red('\n❌ Original:'));
|
|
75
|
-
console.log(chalk_1.default.gray(
|
|
74
|
+
console.log(chalk_1.default.gray(original));
|
|
76
75
|
console.log(chalk_1.default.green('\n✅ Optimized:'));
|
|
77
|
-
console.log(chalk_1.default.white(
|
|
78
|
-
console.log(chalk_1.default.gray('\n' + '─'.repeat(60)));
|
|
76
|
+
console.log(chalk_1.default.white(optimized));
|
|
79
77
|
}
|
|
80
78
|
//# sourceMappingURL=display.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
// Example test file - you can expand this with actual tests
|
|
5
|
+
(0, vitest_1.describe)('Display Utils', () => {
|
|
6
|
+
(0, vitest_1.it)('should run a simple test', () => {
|
|
7
|
+
(0, vitest_1.expect)(true).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
//# sourceMappingURL=display.test.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
|
2
|
+
import tsParser from '@typescript-eslint/parser';
|
|
3
|
+
|
|
4
|
+
export default [
|
|
5
|
+
{
|
|
6
|
+
files: ['src/**/*.ts'],
|
|
7
|
+
languageOptions: {
|
|
8
|
+
parser: tsParser,
|
|
9
|
+
parserOptions: {
|
|
10
|
+
ecmaVersion: 2020,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
},
|
|
13
|
+
globals: {
|
|
14
|
+
process: 'readonly',
|
|
15
|
+
console: 'readonly',
|
|
16
|
+
__dirname: 'readonly',
|
|
17
|
+
require: 'readonly',
|
|
18
|
+
module: 'readonly',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
plugins: {
|
|
22
|
+
'@typescript-eslint': tsPlugin,
|
|
23
|
+
},
|
|
24
|
+
rules: {
|
|
25
|
+
...tsPlugin.configs.recommended.rules,
|
|
26
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
27
|
+
'@typescript-eslint/no-unused-vars': [
|
|
28
|
+
'error',
|
|
29
|
+
{ argsIgnorePattern: '^_' },
|
|
30
|
+
],
|
|
31
|
+
'no-console': 'off',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
ignores: ['dist/**', 'node_modules/**', '**/*.js'],
|
|
36
|
+
},
|
|
37
|
+
];
|