english-optimizer-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/README.md +487 -0
- package/config.yaml +38 -0
- package/dist/ai/api-provider.js +138 -0
- package/dist/ai/ollama.js +103 -0
- package/dist/ai/provider.js +26 -0
- package/dist/config/config.js +223 -0
- package/dist/core/batch.js +45 -0
- package/dist/core/editor.js +175 -0
- package/dist/core/instant-editor.js +149 -0
- package/dist/core/optimizer.js +74 -0
- package/dist/history/logger.js +57 -0
- package/dist/index.js +279 -0
- package/dist/prompts/custom.js +75 -0
- package/dist/prompts/templates.js +59 -0
- package/dist/prompts/translation-prompt.js +57 -0
- package/dist/prompts/yaml-prompt.js +101 -0
- package/dist/types/index.js +11 -0
- package/dist/utils/display.js +80 -0
- package/docker-compose.yml +22 -0
- package/package.json +54 -0
- package/prompt.yaml +48 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.InstantEditor = void 0;
|
|
7
|
+
const readline_1 = __importDefault(require("readline"));
|
|
8
|
+
const process_1 = require("process");
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const clipboardy_1 = __importDefault(require("clipboardy"));
|
|
11
|
+
const translation_prompt_1 = require("../prompts/translation-prompt");
|
|
12
|
+
const yaml_prompt_1 = require("../prompts/yaml-prompt");
|
|
13
|
+
const display_1 = require("../utils/display");
|
|
14
|
+
class InstantEditor {
|
|
15
|
+
constructor(optimizer, historyLogger, promptPath) {
|
|
16
|
+
this.useYAMLPrompt = false;
|
|
17
|
+
this.currentText = '';
|
|
18
|
+
this.pendingLines = [];
|
|
19
|
+
this.optimizer = optimizer;
|
|
20
|
+
this.historyLogger = historyLogger;
|
|
21
|
+
this.promptLoader = new translation_prompt_1.TranslationPromptLoader(promptPath);
|
|
22
|
+
// Check for YAML prompt
|
|
23
|
+
this.yamlPromptLoader = new yaml_prompt_1.YAMLPromptLoader(promptPath);
|
|
24
|
+
this.useYAMLPrompt = this.yamlPromptLoader.hasYAMLPrompt();
|
|
25
|
+
if (this.useYAMLPrompt) {
|
|
26
|
+
console.log(chalk_1.default.cyan('✓ Using YAML prompt configuration from: ' + this.yamlPromptLoader.getPromptPath()));
|
|
27
|
+
}
|
|
28
|
+
this.rl = readline_1.default.createInterface({
|
|
29
|
+
input: process_1.stdin,
|
|
30
|
+
output: process_1.stdout,
|
|
31
|
+
terminal: true,
|
|
32
|
+
prompt: '> ',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async start() {
|
|
36
|
+
this.displayWelcome();
|
|
37
|
+
this.displayInstructions();
|
|
38
|
+
console.log(chalk_1.default.cyan('\n✨ Ready! Start typing...\n'));
|
|
39
|
+
this.rl.prompt();
|
|
40
|
+
this.rl.on('line', async (line) => {
|
|
41
|
+
if (line.trim() === '') {
|
|
42
|
+
// Empty line - trigger optimization
|
|
43
|
+
if (this.pendingLines.length > 0) {
|
|
44
|
+
await this.translateAndOptimize();
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.rl.prompt();
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// Accumulate text
|
|
52
|
+
this.pendingLines.push(line);
|
|
53
|
+
this.currentText = this.pendingLines.join('\n');
|
|
54
|
+
console.log(chalk_1.default.gray(` ✓ Line ${this.pendingLines.length} added. Press Enter (empty line) to optimize`));
|
|
55
|
+
this.rl.prompt();
|
|
56
|
+
});
|
|
57
|
+
this.rl.on('SIGINT', () => {
|
|
58
|
+
console.log('\n\n' + chalk_1.default.gray('─'.repeat(60)));
|
|
59
|
+
console.log(chalk_1.default.cyan('\n👋 Goodbye!\n'));
|
|
60
|
+
this.close();
|
|
61
|
+
process.exit(0);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async translateAndOptimize() {
|
|
65
|
+
const fullText = this.currentText.trim() || this.pendingLines.join('\n').trim();
|
|
66
|
+
if (!fullText) {
|
|
67
|
+
console.log(chalk_1.default.yellow('\n⚠️ No text to optimize. Type something first!\n'));
|
|
68
|
+
this.rl.prompt();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
console.log(chalk_1.default.cyan('\n🔄 Translating and optimizing...\n'));
|
|
73
|
+
let result;
|
|
74
|
+
if (this.useYAMLPrompt && this.yamlPromptLoader) {
|
|
75
|
+
// Use YAML prompt
|
|
76
|
+
const prompt = this.yamlPromptLoader.buildPrompt(fullText);
|
|
77
|
+
result = await this.optimizer.provider.generateWithPrompt(prompt);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Use text-based prompt
|
|
81
|
+
const promptTemplate = this.promptLoader.getPrompt();
|
|
82
|
+
const prompt = promptTemplate.replace('{text}', fullText);
|
|
83
|
+
result = await this.optimizer.provider.generateWithPrompt(prompt);
|
|
84
|
+
}
|
|
85
|
+
// Show bilingual comparison
|
|
86
|
+
this.displayBilingualResult(fullText, result);
|
|
87
|
+
// Clear pending lines for next input
|
|
88
|
+
this.pendingLines = [];
|
|
89
|
+
this.currentText = '';
|
|
90
|
+
console.log(chalk_1.default.cyan('\n✨ Ready for next input!\n'));
|
|
91
|
+
this.rl.prompt();
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
(0, display_1.displayError)(error);
|
|
95
|
+
console.log(chalk_1.default.cyan('\nContinue typing...\n'));
|
|
96
|
+
this.rl.prompt();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
displayBilingualResult(original, optimized) {
|
|
100
|
+
console.log(chalk_1.default.gray('\n' + '═'.repeat(70)));
|
|
101
|
+
console.log(chalk_1.default.cyan.bold('\n📝 中英文对照 / Bilingual Comparison:\n'));
|
|
102
|
+
console.log(chalk_1.default.yellow('Original / 原文:'));
|
|
103
|
+
console.log(chalk_1.default.gray(original));
|
|
104
|
+
console.log(chalk_1.default.green('\nOptimized English / 优化后的英文:'));
|
|
105
|
+
console.log(chalk_1.default.white.bold(optimized));
|
|
106
|
+
console.log(chalk_1.default.gray('\n' + '═'.repeat(70)));
|
|
107
|
+
// Copy to clipboard
|
|
108
|
+
try {
|
|
109
|
+
clipboardy_1.default.writeSync(optimized);
|
|
110
|
+
console.log(chalk_1.default.green.bold('\n✓ 已复制到剪贴板 / Copied to clipboard!\n'));
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.log(chalk_1.default.yellow('\n⚠️ 剪贴板复制失败 / Failed to copy to clipboard\n'));
|
|
114
|
+
}
|
|
115
|
+
// Save to history
|
|
116
|
+
if (this.historyLogger) {
|
|
117
|
+
this.historyLogger.addEntry({
|
|
118
|
+
original,
|
|
119
|
+
optimized,
|
|
120
|
+
mode: 'professional',
|
|
121
|
+
timestamp: new Date(),
|
|
122
|
+
provider: 'Translation',
|
|
123
|
+
model: 'Bilingual',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
displayWelcome() {
|
|
128
|
+
console.log(chalk_1.default.cyan.bold('\n🚀 English Optimizer CLI - 中英文优化模式\n'));
|
|
129
|
+
}
|
|
130
|
+
displayInstructions() {
|
|
131
|
+
console.log(chalk_1.default.white.bold('使用说明 / How to use:'));
|
|
132
|
+
console.log(chalk_1.default.gray('1. 输入你的内容(中文或英文)/ Type your content (Chinese or English)'));
|
|
133
|
+
console.log(chalk_1.default.gray('2. 每行结束后按 Enter / Press Enter after each line'));
|
|
134
|
+
console.log(chalk_1.default.white('3. 输入完成后,按 ' + chalk_1.default.cyan.bold('Enter (空行)') + ' 触发优化'));
|
|
135
|
+
console.log(chalk_1.default.white(' When done, press ' + chalk_1.default.cyan.bold('Enter (empty line)') + ' to translate & optimize'));
|
|
136
|
+
console.log(chalk_1.default.gray('4. 查看中英文对照结果 / See bilingual result'));
|
|
137
|
+
console.log(chalk_1.default.green('5. 优化后的英文会自动复制到剪贴板 / Optimized English auto-copied to clipboard'));
|
|
138
|
+
console.log(chalk_1.default.yellow('\n提示词配置 / Prompt Config:'));
|
|
139
|
+
console.log(chalk_1.default.gray(` 编辑自定义提示词: ${this.promptLoader.getPromptPath()}`));
|
|
140
|
+
console.log(chalk_1.default.gray(' Edit custom prompt: ' + this.promptLoader.getPromptPath()));
|
|
141
|
+
console.log(chalk_1.default.yellow('\n其他 / Other:'));
|
|
142
|
+
console.log(chalk_1.default.gray(' Ctrl+C - 退出 / Quit\n'));
|
|
143
|
+
}
|
|
144
|
+
close() {
|
|
145
|
+
this.rl.close();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.InstantEditor = InstantEditor;
|
|
149
|
+
//# sourceMappingURL=instant-editor.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Optimizer = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
const yaml_prompt_1 = require("../prompts/yaml-prompt");
|
|
6
|
+
class Optimizer {
|
|
7
|
+
constructor(provider, historyLogger, useYAMLPrompt = false) {
|
|
8
|
+
this.provider = provider;
|
|
9
|
+
this.historyLogger = historyLogger;
|
|
10
|
+
if (useYAMLPrompt) {
|
|
11
|
+
this.yamlPromptLoader = new yaml_prompt_1.YAMLPromptLoader();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async optimize(text, mode) {
|
|
15
|
+
const startTime = Date.now();
|
|
16
|
+
const optimized = await this.provider.optimize(text, mode);
|
|
17
|
+
const duration = Date.now() - startTime;
|
|
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;
|
|
31
|
+
}
|
|
32
|
+
async optimizeWithYAMLPrompt(text) {
|
|
33
|
+
if (!this.yamlPromptLoader || !this.yamlPromptLoader.hasYAMLPrompt()) {
|
|
34
|
+
throw new Error('YAML prompt not available');
|
|
35
|
+
}
|
|
36
|
+
const prompt = this.yamlPromptLoader.buildPrompt(text);
|
|
37
|
+
const optimized = await this.provider.generateWithPrompt(prompt);
|
|
38
|
+
const result = {
|
|
39
|
+
original: text,
|
|
40
|
+
optimized,
|
|
41
|
+
mode: types_1.OptimizationMode.PROFESSIONAL,
|
|
42
|
+
timestamp: new Date(),
|
|
43
|
+
provider: this.getProviderName(),
|
|
44
|
+
model: this.getModelName(),
|
|
45
|
+
};
|
|
46
|
+
// Save to history if enabled
|
|
47
|
+
if (this.historyLogger) {
|
|
48
|
+
this.historyLogger.addEntry(result);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
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
|
+
getProviderName() {
|
|
67
|
+
return this.provider.constructor.name;
|
|
68
|
+
}
|
|
69
|
+
getModelName() {
|
|
70
|
+
return 'unknown';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.Optimizer = Optimizer;
|
|
74
|
+
//# sourceMappingURL=optimizer.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HistoryLogger = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const uuid_1 = require("uuid");
|
|
7
|
+
class HistoryLogger {
|
|
8
|
+
constructor(historyPath) {
|
|
9
|
+
this.historyPath = historyPath;
|
|
10
|
+
this.ensureHistoryFile();
|
|
11
|
+
}
|
|
12
|
+
ensureHistoryFile() {
|
|
13
|
+
const dir = (0, path_1.dirname)(this.historyPath);
|
|
14
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
15
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
if (!(0, fs_1.existsSync)(this.historyPath)) {
|
|
18
|
+
(0, fs_1.writeFileSync)(this.historyPath, JSON.stringify([], null, 2), 'utf-8');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
addEntry(result) {
|
|
22
|
+
const history = this.getHistory();
|
|
23
|
+
const entry = {
|
|
24
|
+
...result,
|
|
25
|
+
id: (0, uuid_1.v4)(),
|
|
26
|
+
};
|
|
27
|
+
history.unshift(entry); // Add to the beginning
|
|
28
|
+
// Keep only the last 100 entries
|
|
29
|
+
if (history.length > 100) {
|
|
30
|
+
history.pop();
|
|
31
|
+
}
|
|
32
|
+
(0, fs_1.writeFileSync)(this.historyPath, JSON.stringify(history, null, 2), 'utf-8');
|
|
33
|
+
return entry;
|
|
34
|
+
}
|
|
35
|
+
getHistory() {
|
|
36
|
+
try {
|
|
37
|
+
const content = (0, fs_1.readFileSync)(this.historyPath, 'utf-8');
|
|
38
|
+
return JSON.parse(content);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
clearHistory() {
|
|
45
|
+
(0, fs_1.writeFileSync)(this.historyPath, JSON.stringify([], null, 2), 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
getEntryById(id) {
|
|
48
|
+
const history = this.getHistory();
|
|
49
|
+
return history.find((entry) => entry.id === id);
|
|
50
|
+
}
|
|
51
|
+
getRecentEntries(count = 10) {
|
|
52
|
+
const history = this.getHistory();
|
|
53
|
+
return history.slice(0, count);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.HistoryLogger = HistoryLogger;
|
|
57
|
+
//# sourceMappingURL=logger.js.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const config_1 = require("./config/config");
|
|
43
|
+
const provider_1 = require("./ai/provider");
|
|
44
|
+
const optimizer_1 = require("./core/optimizer");
|
|
45
|
+
const editor_1 = require("./core/editor");
|
|
46
|
+
const instant_editor_1 = require("./core/instant-editor");
|
|
47
|
+
const batch_1 = require("./core/batch");
|
|
48
|
+
const logger_1 = require("./history/logger");
|
|
49
|
+
const custom_1 = require("./prompts/custom");
|
|
50
|
+
const display_1 = require("./utils/display");
|
|
51
|
+
const program = new commander_1.Command();
|
|
52
|
+
program
|
|
53
|
+
.name('fuck-abc')
|
|
54
|
+
.description('CLI tool to help non-native English speakers improve their writing using AI')
|
|
55
|
+
.version('1.0.0');
|
|
56
|
+
program
|
|
57
|
+
.command('start', { isDefault: true })
|
|
58
|
+
.description('Start interactive optimization mode')
|
|
59
|
+
.option('-c, --classic', 'Use classic mode (submit text then optimize)', false)
|
|
60
|
+
.action(async (options) => {
|
|
61
|
+
try {
|
|
62
|
+
const config = config_1.configManager.getConfig();
|
|
63
|
+
// Initialize provider
|
|
64
|
+
const provider = (0, provider_1.createProvider)(config);
|
|
65
|
+
// Check if provider is available
|
|
66
|
+
const isAvailable = await provider.isAvailable();
|
|
67
|
+
if (!isAvailable) {
|
|
68
|
+
(0, display_1.displayError)(config.ai.provider === 'ollama'
|
|
69
|
+
? 'Ollama is not running. Please start Ollama or run: docker-compose up -d'
|
|
70
|
+
: 'API provider is not available. Please check your API key and configuration.');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
// Initialize optional features
|
|
74
|
+
let historyLogger;
|
|
75
|
+
if (config.features.enableHistory) {
|
|
76
|
+
config_1.ConfigManager.ensureConfigDir();
|
|
77
|
+
historyLogger = new logger_1.HistoryLogger(config.features.historyPath);
|
|
78
|
+
}
|
|
79
|
+
let customPromptLoader;
|
|
80
|
+
if (config.features.enableCustomPrompts) {
|
|
81
|
+
config_1.ConfigManager.ensureConfigDir();
|
|
82
|
+
customPromptLoader = new custom_1.CustomPromptLoader(config.features.customPromptsPath);
|
|
83
|
+
}
|
|
84
|
+
// Initialize optimizer
|
|
85
|
+
const optimizer = new optimizer_1.Optimizer(provider, historyLogger);
|
|
86
|
+
// Start editor (instant mode by default, or classic mode with flag)
|
|
87
|
+
if (options.classic) {
|
|
88
|
+
const editor = new editor_1.InteractiveEditor(optimizer, historyLogger, customPromptLoader);
|
|
89
|
+
await editor.start();
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const editor = new instant_editor_1.InstantEditor(optimizer, historyLogger);
|
|
93
|
+
await editor.start();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
(0, display_1.displayError)(error);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
program
|
|
102
|
+
.command('history')
|
|
103
|
+
.description('View optimization history')
|
|
104
|
+
.option('-n, --number <count>', 'Number of recent entries to show', '10')
|
|
105
|
+
.action(async (options) => {
|
|
106
|
+
try {
|
|
107
|
+
const config = config_1.configManager.getConfig();
|
|
108
|
+
if (!config.features.enableHistory) {
|
|
109
|
+
(0, display_1.displayInfo)('History is disabled in configuration.');
|
|
110
|
+
process.exit(0);
|
|
111
|
+
}
|
|
112
|
+
const historyLogger = new logger_1.HistoryLogger(config.features.historyPath);
|
|
113
|
+
const history = historyLogger.getRecentEntries(parseInt(options.number));
|
|
114
|
+
if (history.length === 0) {
|
|
115
|
+
(0, display_1.displayInfo)('No history yet.');
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
console.log(`\nShowing ${history.length} recent optimizations:\n`);
|
|
119
|
+
for (const entry of history) {
|
|
120
|
+
console.log('─'.repeat(60));
|
|
121
|
+
console.log(`ID: ${entry.id}`);
|
|
122
|
+
console.log(`Time: ${entry.timestamp.toLocaleString()}`);
|
|
123
|
+
console.log(`Mode: ${entry.mode}`);
|
|
124
|
+
console.log(`Original: ${entry.original.substring(0, 100)}...`);
|
|
125
|
+
console.log(`Optimized: ${entry.optimized.substring(0, 100)}...`);
|
|
126
|
+
console.log('');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
(0, display_1.displayError)(error);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
program
|
|
135
|
+
.command('config')
|
|
136
|
+
.description('Show current configuration')
|
|
137
|
+
.action(() => {
|
|
138
|
+
try {
|
|
139
|
+
const config = config_1.configManager.getConfig();
|
|
140
|
+
console.log(JSON.stringify(config, null, 2));
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
(0, display_1.displayError)(error);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
program
|
|
148
|
+
.command('prompts')
|
|
149
|
+
.description('List custom prompts')
|
|
150
|
+
.action(() => {
|
|
151
|
+
try {
|
|
152
|
+
const config = config_1.configManager.getConfig();
|
|
153
|
+
const customPromptLoader = new custom_1.CustomPromptLoader(config.features.customPromptsPath);
|
|
154
|
+
const prompts = customPromptLoader.getCustomPrompts();
|
|
155
|
+
if (prompts.length === 0) {
|
|
156
|
+
(0, display_1.displayInfo)('No custom prompts found.');
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
console.log('\nCustom Prompts:\n');
|
|
160
|
+
for (const prompt of prompts) {
|
|
161
|
+
console.log(`Name: ${prompt.name}`);
|
|
162
|
+
console.log(`Description: ${prompt.description}`);
|
|
163
|
+
console.log(`Hotkey: ${prompt.hotkey || 'None'}`);
|
|
164
|
+
console.log('');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
(0, display_1.displayError)(error);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
program
|
|
173
|
+
.command('prompt')
|
|
174
|
+
.description('Manage translation prompt template')
|
|
175
|
+
.option('-e, --edit', 'Edit the prompt file', false)
|
|
176
|
+
.option('-s, --show', 'Show the current prompt', false)
|
|
177
|
+
.action(async (options) => {
|
|
178
|
+
try {
|
|
179
|
+
const { TranslationPromptLoader } = await Promise.resolve().then(() => __importStar(require('./prompts/translation-prompt')));
|
|
180
|
+
const { YAMLPromptLoader } = await Promise.resolve().then(() => __importStar(require('./prompts/yaml-prompt')));
|
|
181
|
+
const yamlPromptLoader = new YAMLPromptLoader();
|
|
182
|
+
const textPromptLoader = new TranslationPromptLoader();
|
|
183
|
+
const hasYAMLPrompt = yamlPromptLoader.hasYAMLPrompt();
|
|
184
|
+
if (hasYAMLPrompt) {
|
|
185
|
+
console.log(chalk_1.default.cyan('\n✓ YAML prompt detected!\n'));
|
|
186
|
+
}
|
|
187
|
+
if (options.show) {
|
|
188
|
+
if (hasYAMLPrompt) {
|
|
189
|
+
console.log(chalk_1.default.cyan('📄 YAML Prompt file: ' + yamlPromptLoader.getPromptPath() + '\n'));
|
|
190
|
+
console.log('─'.repeat(70));
|
|
191
|
+
console.log(yamlPromptLoader.loadConfig());
|
|
192
|
+
console.log('─'.repeat(70));
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.log(`\n📄 Prompt file location: ${textPromptLoader.getPromptPath()}\n`);
|
|
196
|
+
console.log('Current prompt:');
|
|
197
|
+
console.log('─'.repeat(70));
|
|
198
|
+
console.log(textPromptLoader.getPrompt());
|
|
199
|
+
console.log('─'.repeat(70));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (options.edit) {
|
|
203
|
+
const { execSync } = require('child_process');
|
|
204
|
+
const promptPath = hasYAMLPrompt
|
|
205
|
+
? yamlPromptLoader.getPromptPath()
|
|
206
|
+
: textPromptLoader.getPromptPath();
|
|
207
|
+
console.log(`\n📝 Opening ${promptPath} in your default editor...\n`);
|
|
208
|
+
try {
|
|
209
|
+
execSync(`"${process.env.EDITOR || 'code'}" "${promptPath}"`, { stdio: 'inherit' });
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
console.log(chalk_1.default.yellow('\n⚠️ Could not open editor. Please edit manually:'));
|
|
213
|
+
console.log(chalk_1.default.gray(promptPath));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
if (hasYAMLPrompt) {
|
|
218
|
+
console.log(`\n📄 YAML Prompt file: ${yamlPromptLoader.getPromptPath()}`);
|
|
219
|
+
console.log(chalk_1.default.cyan('\n✓ Using YAML prompt configuration'));
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
console.log(`\n📄 Prompt file location: ${textPromptLoader.getPromptPath()}`);
|
|
223
|
+
}
|
|
224
|
+
console.log(chalk_1.default.gray('\nCommands:'));
|
|
225
|
+
console.log(' fuck-abc prompt --show Show current prompt');
|
|
226
|
+
console.log(' fuck-abc prompt --edit Edit prompt file');
|
|
227
|
+
console.log(chalk_1.default.gray('\nEdit the file directly to customize translation behavior.\n'));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
(0, display_1.displayError)(error);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
program
|
|
236
|
+
.command('batch')
|
|
237
|
+
.description('Batch optimize files')
|
|
238
|
+
.argument('<files...>', 'Files to optimize')
|
|
239
|
+
.option('-m, --mode <mode>', 'Optimization mode (professional, concise, grammar, senior_developer)', 'professional')
|
|
240
|
+
.option('-i, --in-place', 'Modify files in place', false)
|
|
241
|
+
.option('-s, --suffix <suffix>', 'Output file suffix', '.optimized')
|
|
242
|
+
.action(async (files, options) => {
|
|
243
|
+
try {
|
|
244
|
+
const config = config_1.configManager.getConfig();
|
|
245
|
+
// Validate mode
|
|
246
|
+
const validModes = ['professional', 'concise', 'grammar', 'senior_developer'];
|
|
247
|
+
if (!validModes.includes(options.mode)) {
|
|
248
|
+
(0, display_1.displayError)(`Invalid mode: ${options.mode}. Valid modes: ${validModes.join(', ')}`);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
// Initialize provider
|
|
252
|
+
const provider = (0, provider_1.createProvider)(config);
|
|
253
|
+
const isAvailable = await provider.isAvailable();
|
|
254
|
+
if (!isAvailable) {
|
|
255
|
+
(0, display_1.displayError)('AI provider is not available.');
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
// Initialize optimizer
|
|
259
|
+
let historyLogger;
|
|
260
|
+
if (config.features.enableHistory) {
|
|
261
|
+
config_1.ConfigManager.ensureConfigDir();
|
|
262
|
+
historyLogger = new logger_1.HistoryLogger(config.features.historyPath);
|
|
263
|
+
}
|
|
264
|
+
const optimizer = new optimizer_1.Optimizer(provider, historyLogger);
|
|
265
|
+
const batchProcessor = new batch_1.BatchProcessor(optimizer);
|
|
266
|
+
await batchProcessor.processBatch({
|
|
267
|
+
files,
|
|
268
|
+
mode: options.mode,
|
|
269
|
+
inPlace: options.inPlace,
|
|
270
|
+
outputSuffix: options.suffix,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
(0, display_1.displayError)(error);
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
program.parse();
|
|
279
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CustomPromptLoader = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const CustomPromptSchema = zod_1.z.object({
|
|
8
|
+
name: zod_1.z.string(),
|
|
9
|
+
description: zod_1.z.string(),
|
|
10
|
+
prompt: zod_1.z.string(),
|
|
11
|
+
hotkey: zod_1.z.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
class CustomPromptLoader {
|
|
14
|
+
constructor(promptsPath) {
|
|
15
|
+
this.promptsPath = promptsPath;
|
|
16
|
+
this.ensurePromptsFile();
|
|
17
|
+
}
|
|
18
|
+
ensurePromptsFile() {
|
|
19
|
+
const dir = (0, path_1.dirname)(this.promptsPath);
|
|
20
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
21
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
if (!(0, fs_1.existsSync)(this.promptsPath)) {
|
|
24
|
+
const examplePrompts = [
|
|
25
|
+
{
|
|
26
|
+
name: 'Academic',
|
|
27
|
+
description: 'Rewrite in an academic style suitable for research papers',
|
|
28
|
+
prompt: 'Please rewrite the following text in an academic style suitable for research papers or scholarly articles. Use formal language, precise terminology, and maintain objectivity.',
|
|
29
|
+
hotkey: 'a',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Friendly',
|
|
33
|
+
description: 'Make the text more friendly and casual',
|
|
34
|
+
prompt: 'Please rewrite the following text to sound more friendly and casual, while maintaining the core message. Use conversational language.',
|
|
35
|
+
hotkey: 'f',
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
(0, fs_1.writeFileSync)(this.promptsPath, JSON.stringify(examplePrompts, null, 2), 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
getCustomPrompts() {
|
|
42
|
+
try {
|
|
43
|
+
const content = (0, fs_1.readFileSync)(this.promptsPath, 'utf-8');
|
|
44
|
+
const prompts = JSON.parse(content);
|
|
45
|
+
return prompts.map((p) => CustomPromptSchema.parse(p));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
getPromptByName(name) {
|
|
52
|
+
const prompts = this.getCustomPrompts();
|
|
53
|
+
return prompts.find((p) => p.name.toLowerCase() === name.toLowerCase());
|
|
54
|
+
}
|
|
55
|
+
getPromptByHotkey(hotkey) {
|
|
56
|
+
const prompts = this.getCustomPrompts();
|
|
57
|
+
return prompts.find((p) => p.hotkey === hotkey);
|
|
58
|
+
}
|
|
59
|
+
addPrompt(prompt) {
|
|
60
|
+
const prompts = this.getCustomPrompts();
|
|
61
|
+
prompts.push(prompt);
|
|
62
|
+
(0, fs_1.writeFileSync)(this.promptsPath, JSON.stringify(prompts, null, 2), 'utf-8');
|
|
63
|
+
}
|
|
64
|
+
removePrompt(name) {
|
|
65
|
+
const prompts = this.getCustomPrompts();
|
|
66
|
+
const filtered = prompts.filter((p) => p.name.toLowerCase() !== name.toLowerCase());
|
|
67
|
+
if (filtered.length === prompts.length) {
|
|
68
|
+
return false; // Nothing was removed
|
|
69
|
+
}
|
|
70
|
+
(0, fs_1.writeFileSync)(this.promptsPath, JSON.stringify(filtered, null, 2), 'utf-8');
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.CustomPromptLoader = CustomPromptLoader;
|
|
75
|
+
//# sourceMappingURL=custom.js.map
|