english-optimizer-cli 1.4.0 ā 1.6.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/.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 +66 -70
- package/dist/ai/ollama.js +23 -45
- package/dist/config/config.js +0 -3
- package/dist/config/test.js +16 -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
|
@@ -5,66 +5,43 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ApiProvider = void 0;
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
9
|
const templates_1 = require("../prompts/templates");
|
|
9
10
|
class ApiProvider {
|
|
10
11
|
constructor(config) {
|
|
11
12
|
this.config = config;
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
temperature: 0.7,
|
|
25
|
-
max_tokens: 2000,
|
|
26
|
-
}, {
|
|
27
|
-
headers: {
|
|
28
|
-
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
29
|
-
'Content-Type': 'application/json',
|
|
30
|
-
},
|
|
31
|
-
timeout: 60000, // 60 seconds timeout
|
|
32
|
-
});
|
|
33
|
-
if (response.data && response.data.choices && response.data.choices.length > 0) {
|
|
34
|
-
let result = response.data.choices[0].message.content.trim();
|
|
35
|
-
// Remove quotes if the model wrapped the response in them
|
|
36
|
-
if (result.startsWith('"') && result.endsWith('"')) {
|
|
37
|
-
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');
|
|
38
23
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
for (const prefix of prefixesToRemove) {
|
|
42
|
-
if (result.startsWith(prefix)) {
|
|
43
|
-
result = result.slice(prefix.length).trim();
|
|
44
|
-
}
|
|
24
|
+
if (status === 401) {
|
|
25
|
+
throw new Error('Invalid API key. Please check your API credentials.');
|
|
45
26
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
throw new Error('Invalid response from API');
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
if (axios_1.default.isAxiosError(error)) {
|
|
52
|
-
const axiosError = error;
|
|
53
|
-
if (axiosError.response) {
|
|
54
|
-
const status = axiosError.response.status;
|
|
55
|
-
if (status === 401) {
|
|
56
|
-
throw new Error('Invalid API key. Please check your API credentials.');
|
|
57
|
-
}
|
|
58
|
-
else if (status === 429) {
|
|
59
|
-
throw new Error('Rate limit exceeded. Please try again later.');
|
|
60
|
-
}
|
|
27
|
+
else if (status === 429) {
|
|
28
|
+
throw new Error('Rate limit exceeded. Please try again later.');
|
|
61
29
|
}
|
|
62
|
-
throw new Error(`API error: ${axiosError.message}`);
|
|
63
30
|
}
|
|
64
|
-
throw error;
|
|
31
|
+
throw new Error(`API error: ${axiosError.message}`);
|
|
65
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);
|
|
66
39
|
}
|
|
67
40
|
async generateWithPrompt(prompt) {
|
|
41
|
+
const result = await this.callAPI(prompt);
|
|
42
|
+
return this.cleanupResponse(result, false);
|
|
43
|
+
}
|
|
44
|
+
async callAPI(prompt) {
|
|
68
45
|
try {
|
|
69
46
|
const response = await axios_1.default.post(`${this.config.baseUrl}/chat/completions`, {
|
|
70
47
|
model: this.config.model,
|
|
@@ -78,58 +55,77 @@ class ApiProvider {
|
|
|
78
55
|
max_tokens: 2000,
|
|
79
56
|
}, {
|
|
80
57
|
headers: {
|
|
81
|
-
|
|
58
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
82
59
|
'Content-Type': 'application/json',
|
|
83
60
|
},
|
|
84
61
|
timeout: 60000,
|
|
85
62
|
});
|
|
86
63
|
if (response.data && response.data.choices && response.data.choices.length > 0) {
|
|
87
|
-
|
|
88
|
-
// Remove quotes if the model wrapped the response in them
|
|
89
|
-
if (result.startsWith('"') && result.endsWith('"')) {
|
|
90
|
-
result = result.slice(1, -1);
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
64
|
+
return response.data.choices[0].message.content.trim();
|
|
93
65
|
}
|
|
94
66
|
throw new Error('Invalid response from API');
|
|
95
67
|
}
|
|
96
68
|
catch (error) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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;
|
|
107
84
|
}
|
|
108
|
-
throw new Error(`API error: ${axiosError.message}`);
|
|
109
85
|
}
|
|
110
|
-
throw error;
|
|
111
86
|
}
|
|
87
|
+
return result;
|
|
112
88
|
}
|
|
113
89
|
async isAvailable() {
|
|
114
90
|
if (!this.config.apiKey) {
|
|
115
91
|
return false;
|
|
116
92
|
}
|
|
117
93
|
try {
|
|
118
|
-
// Try a simple API call to check if
|
|
94
|
+
// Try a simple API call to check if key is valid
|
|
119
95
|
const response = await axios_1.default.post(`${this.config.baseUrl}/chat/completions`, {
|
|
120
96
|
model: this.config.model,
|
|
121
97
|
messages: [{ role: 'user', content: 'test' }],
|
|
122
98
|
max_tokens: 5,
|
|
123
99
|
}, {
|
|
124
100
|
headers: {
|
|
125
|
-
|
|
101
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
126
102
|
'Content-Type': 'application/json',
|
|
127
103
|
},
|
|
128
104
|
timeout: 10000,
|
|
129
105
|
});
|
|
130
106
|
return response.status === 200;
|
|
131
107
|
}
|
|
132
|
-
catch {
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (error.response) {
|
|
110
|
+
console.log('\n' + chalk_1.default.yellow('API connection failed with status:'), error.response.status);
|
|
111
|
+
if (error.response.data) {
|
|
112
|
+
console.log(chalk_1.default.yellow('Error details:'), JSON.stringify(error.response.data));
|
|
113
|
+
// Throw error for GLM balance issues
|
|
114
|
+
if (error.response.data?.error?.code === '1113') {
|
|
115
|
+
throw new Error('GLM account balance is insufficient. Please recharge your account at https://open.bigmodel.cn/usercenter/finance');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (error.request) {
|
|
120
|
+
console.log('\n' + chalk_1.default.yellow('No response from API'));
|
|
121
|
+
console.log(chalk_1.default.gray('Possible issues:'));
|
|
122
|
+
console.log(chalk_1.default.gray(' - Network connectivity'));
|
|
123
|
+
console.log(chalk_1.default.gray(' - API URL is incorrect'));
|
|
124
|
+
console.log(chalk_1.default.gray(' - Firewall blocking the request'));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.log('\n' + chalk_1.default.yellow('Request setup error:'), error.message);
|
|
128
|
+
}
|
|
133
129
|
return false;
|
|
134
130
|
}
|
|
135
131
|
}
|
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
|
@@ -25,7 +25,22 @@ async function testConfiguration() {
|
|
|
25
25
|
}
|
|
26
26
|
console.log(chalk_1.default.gray('\n' + 'ā'.repeat(50) + '\n'));
|
|
27
27
|
console.log(chalk_1.default.cyan('š Testing connection...\n'));
|
|
28
|
-
|
|
28
|
+
let isAvailable = false;
|
|
29
|
+
try {
|
|
30
|
+
isAvailable = await provider.isAvailable();
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
if (e.message.includes('balance is insufficient')) {
|
|
34
|
+
console.log(chalk_1.default.red.bold('\nā GLM Account Balance Issue!\n'));
|
|
35
|
+
console.log(chalk_1.default.yellow('Your GLM account has insufficient balance.\n'));
|
|
36
|
+
console.log(chalk_1.default.cyan('š” To fix:'));
|
|
37
|
+
console.log(chalk_1.default.white(' 1. Visit https://open.bigmodel.cn/usercenter/finance'));
|
|
38
|
+
console.log(chalk_1.default.white(' 2. Recharge your account'));
|
|
39
|
+
console.log(chalk_1.default.white(' 3. Run test again: fuck-abc test\n'));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
throw e;
|
|
43
|
+
}
|
|
29
44
|
if (isAvailable) {
|
|
30
45
|
console.log(chalk_1.default.green.bold('ā
API configuration is valid!\n'));
|
|
31
46
|
if (config.ai.provider === 'api') {
|
|
@@ -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
|