@involvex/prompt-enhancer 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +9 -0
- package/LICENSE +21 -0
- package/dist/app.d.ts +5 -0
- package/dist/app.js +39 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +53 -0
- package/dist/commands/about.d.ts +1 -0
- package/dist/commands/about.js +6 -0
- package/dist/commands/direct-enhance.d.ts +5 -0
- package/dist/commands/direct-enhance.js +52 -0
- package/dist/commands/enhanceprompt.d.ts +1 -0
- package/dist/commands/enhanceprompt.js +6 -0
- package/dist/commands/help.d.ts +1 -0
- package/dist/commands/help.js +6 -0
- package/dist/commands/settings.d.ts +1 -0
- package/dist/commands/settings.js +1 -0
- package/dist/commands/version.d.ts +1 -0
- package/dist/commands/version.js +6 -0
- package/dist/components/enhance-prompt.d.ts +5 -0
- package/dist/components/enhance-prompt.js +82 -0
- package/dist/components/history-viewer.d.ts +5 -0
- package/dist/components/history-viewer.js +64 -0
- package/dist/components/menu.d.ts +11 -0
- package/dist/components/menu.js +6 -0
- package/dist/components/select-input.d.ts +19 -0
- package/dist/components/select-input.js +51 -0
- package/dist/components/settings.d.ts +5 -0
- package/dist/components/settings.js +246 -0
- package/dist/lib/config/manager.d.ts +49 -0
- package/dist/lib/config/manager.js +152 -0
- package/dist/lib/config/schema.d.ts +69 -0
- package/dist/lib/config/schema.js +37 -0
- package/dist/lib/config/types.d.ts +34 -0
- package/dist/lib/config/types.js +4 -0
- package/dist/lib/enhancement/engine.d.ts +41 -0
- package/dist/lib/enhancement/engine.js +163 -0
- package/dist/lib/history/manager.d.ts +45 -0
- package/dist/lib/history/manager.js +120 -0
- package/dist/lib/providers/base.d.ts +47 -0
- package/dist/lib/providers/base.js +30 -0
- package/dist/lib/providers/copilot.d.ts +18 -0
- package/dist/lib/providers/copilot.js +112 -0
- package/dist/lib/providers/gemini.d.ts +13 -0
- package/dist/lib/providers/gemini.js +86 -0
- package/dist/lib/providers/index.d.ts +26 -0
- package/dist/lib/providers/index.js +86 -0
- package/dist/lib/providers/kilo.d.ts +22 -0
- package/dist/lib/providers/kilo.js +174 -0
- package/dist/lib/providers/opencode.d.ts +22 -0
- package/dist/lib/providers/opencode.js +174 -0
- package/dist/lib/types/index.d.ts +26 -0
- package/dist/lib/types/index.js +4 -0
- package/dist/lib/utils/copilot-auth.d.ts +14 -0
- package/dist/lib/utils/copilot-auth.js +84 -0
- package/dist/lib/utils/models-cache.d.ts +12 -0
- package/dist/lib/utils/models-cache.js +139 -0
- package/dist/lib/utils/paths.d.ts +10 -0
- package/dist/lib/utils/paths.js +18 -0
- package/package.json +101 -0
- package/readme.md +271 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# These are supported funding model platforms
|
|
2
|
+
|
|
3
|
+
github: [involvex]
|
|
4
|
+
custom:
|
|
5
|
+
[
|
|
6
|
+
'https://buymeacoffee.com/involvex',
|
|
7
|
+
'https://paypal.me/involvex',
|
|
8
|
+
'https://rewards.bing.com/welcome?rh=14525F68&ref=rafsrchae&form=ML2XE3&OCID=ML2XE3&PUBL=RewardsDO&CREA=ML2XE3',
|
|
9
|
+
]
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 involvex
|
|
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.
|
package/dist/app.d.ts
ADDED
package/dist/app.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import SelectInput from './components/select-input.js';
|
|
5
|
+
import EnhancePromptComponent from './components/enhance-prompt.js';
|
|
6
|
+
import SettingsComponent from './components/settings.js';
|
|
7
|
+
import HistoryComponent from './components/history-viewer.js';
|
|
8
|
+
export default function App({ prompt: _prompt }) {
|
|
9
|
+
const [state, setState] = useState('menu');
|
|
10
|
+
const menuItems = [
|
|
11
|
+
{ label: 'Enhance Prompt', value: 'enhance' },
|
|
12
|
+
{ label: 'Settings', value: 'settings' },
|
|
13
|
+
{ label: 'View History', value: 'history' },
|
|
14
|
+
{ label: 'Help', value: 'help' },
|
|
15
|
+
{ label: 'Exit', value: 'exit' },
|
|
16
|
+
];
|
|
17
|
+
const handleMenuSelect = (value) => {
|
|
18
|
+
if (value === 'exit') {
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
setState(value);
|
|
22
|
+
};
|
|
23
|
+
if (state === 'menu') {
|
|
24
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginY: 1, paddingX: 2, children: _jsx(Text, { bold: true, color: "green", children: "\uD83D\uDE80 Prompt Enhancer" }) }), _jsx(Box, { marginY: 0, paddingX: 2, children: _jsx(Text, { color: "gray", children: "Enhance your prompts with various LLM providers" }) }), _jsx(Box, { marginY: 1, paddingX: 2, children: _jsx(SelectInput, { items: menuItems, onSelect: item => handleMenuSelect(item.value), initialIndex: 0 }) })] }));
|
|
25
|
+
}
|
|
26
|
+
if (state === 'enhance') {
|
|
27
|
+
return _jsx(EnhancePromptComponent, { onBack: () => setState('menu') });
|
|
28
|
+
}
|
|
29
|
+
if (state === 'settings') {
|
|
30
|
+
return _jsx(SettingsComponent, { onBack: () => setState('menu') });
|
|
31
|
+
}
|
|
32
|
+
if (state === 'history') {
|
|
33
|
+
return _jsx(HistoryComponent, { onBack: () => setState('menu') });
|
|
34
|
+
}
|
|
35
|
+
if (state === 'help') {
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { bold: true, color: "cyan", children: "Help" }), _jsx(Text, { children: "Prompt Enhancer is a CLI/TUI tool to enhance prompts using various LLM providers." }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Usage:" }), _jsx(Text, { children: "Interactive mode (TUI): prompt-enhancer" }), _jsx(Text, { children: "CLI mode: prompt-enhancer -p \"your prompt\"" }), _jsx(Text, { children: "Settings: Configure API keys in the Settings menu" }), _jsx(Text, { children: "History: View past enhancements in the History menu" }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Supported Providers:" }), _jsx(Text, { children: "- Gemini (requires API key)" }), _jsx(Text, { children: "- Copilot (requires API key)" }), _jsx(Text, { children: "- Kilo (free tier available)" }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Features:" }), _jsx(Text, { children: "- Real-time streaming output" }), _jsx(Text, { children: "- Persistent configuration (~/.prompt-enhancer/config.json)" }), _jsx(Text, { children: "- Enhancement history (~/.prompt-enhancer/history.json)" }), _jsx(Text, { children: "- Support for multiple providers" }), _jsx(Text, {}), _jsx(Text, { color: "gray", children: "(Press Ctrl+C to return to menu)" })] }));
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import meow from 'meow';
|
|
5
|
+
import App from './app.js';
|
|
6
|
+
import Help from './commands/help.js';
|
|
7
|
+
import hasFlag from 'has-flag';
|
|
8
|
+
import About from './commands/about.js';
|
|
9
|
+
import DirectEnhance from './commands/direct-enhance.js';
|
|
10
|
+
// Handle Ctrl+C globally to exit from any screen
|
|
11
|
+
process.on('SIGINT', () => {
|
|
12
|
+
process.exit(0);
|
|
13
|
+
});
|
|
14
|
+
const cli = meow(`
|
|
15
|
+
Usage
|
|
16
|
+
$ prompt-enhancer
|
|
17
|
+
|
|
18
|
+
Options
|
|
19
|
+
--prompt,-p Your prompt to enhance
|
|
20
|
+
|
|
21
|
+
Examples
|
|
22
|
+
$ prompt-enhancer
|
|
23
|
+
$ prompt-enhancer --prompt="Your prompt here"
|
|
24
|
+
$ prompt-enhancer -p "Your prompt here"
|
|
25
|
+
`, {
|
|
26
|
+
importMeta: import.meta,
|
|
27
|
+
flags: {
|
|
28
|
+
prompt: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
shortFlag: 'p',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
if (cli.input[0] === 'help' || hasFlag('--help')) {
|
|
35
|
+
render(_jsx(Help, {}));
|
|
36
|
+
}
|
|
37
|
+
else if (cli.flags.prompt) {
|
|
38
|
+
// Direct enhancement mode when --prompt/-p flag is provided
|
|
39
|
+
render(_jsx(DirectEnhance, { prompt: cli.flags.prompt }));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Interactive TUI mode (default)
|
|
43
|
+
render(_jsx(App, { prompt: "" }));
|
|
44
|
+
}
|
|
45
|
+
if (hasFlag('--debug')) {
|
|
46
|
+
console.log('Debug flags:', cli.flags);
|
|
47
|
+
}
|
|
48
|
+
if (hasFlag('--version')) {
|
|
49
|
+
await import('./commands/version.js');
|
|
50
|
+
}
|
|
51
|
+
if (hasFlag('--about')) {
|
|
52
|
+
render(_jsx(About, {}));
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function About(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import pkg from '../../package.json' with { type: 'json' };
|
|
3
|
+
import { Text, Box } from 'ink';
|
|
4
|
+
export default function About() {
|
|
5
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: pkg.description }), _jsxs(Text, { children: ["Version: ", pkg.version] }), _jsxs(Text, { children: ["Author: ", pkg.author] }), _jsxs(Text, { children: ["License: ", pkg.license] }), _jsxs(Text, { children: ["Repository: ", pkg.repository.url] })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Text, Box } from 'ink';
|
|
4
|
+
import { ConfigManager } from '../lib/config/manager.js';
|
|
5
|
+
import { HistoryManager } from '../lib/history/manager.js';
|
|
6
|
+
import { EnhancementEngine } from '../lib/enhancement/engine.js';
|
|
7
|
+
export default function DirectEnhance({ prompt }) {
|
|
8
|
+
const [status, setStatus] = useState('initializing');
|
|
9
|
+
const [output, setOutput] = useState('');
|
|
10
|
+
const [error, setError] = useState('');
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
(async () => {
|
|
13
|
+
try {
|
|
14
|
+
// Initialize managers
|
|
15
|
+
const configManager = new ConfigManager();
|
|
16
|
+
await configManager.load(); // Load config from file
|
|
17
|
+
const historyManager = new HistoryManager();
|
|
18
|
+
await historyManager.load(); // Load history from file
|
|
19
|
+
const engine = new EnhancementEngine(configManager, historyManager);
|
|
20
|
+
setStatus('enhancing');
|
|
21
|
+
// Stream enhancement
|
|
22
|
+
let result = '';
|
|
23
|
+
const generator = engine.enhanceStream({
|
|
24
|
+
prompt,
|
|
25
|
+
saveToHistory: true,
|
|
26
|
+
});
|
|
27
|
+
for await (const chunk of generator) {
|
|
28
|
+
result += chunk;
|
|
29
|
+
setOutput(result);
|
|
30
|
+
}
|
|
31
|
+
if (!result.trim()) {
|
|
32
|
+
throw new Error('Enhancement returned an empty result — the model may be rate-limited or temporarily unavailable.');
|
|
33
|
+
}
|
|
34
|
+
setStatus('complete');
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
setStatus('error');
|
|
38
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
39
|
+
}
|
|
40
|
+
})();
|
|
41
|
+
}, [prompt]);
|
|
42
|
+
if (status === 'error') {
|
|
43
|
+
return (_jsx(Box, { flexDirection: "column", marginY: 1, children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) }));
|
|
44
|
+
}
|
|
45
|
+
if (status === 'initializing') {
|
|
46
|
+
return _jsx(Text, { children: "Initializing..." });
|
|
47
|
+
}
|
|
48
|
+
if (status === 'enhancing') {
|
|
49
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Enhancing prompt..." }), _jsx(Text, { children: output })] }));
|
|
50
|
+
}
|
|
51
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, color: "green", children: "Enhanced prompt:" }), _jsx(Text, { children: output })] }));
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function EnhancePrompt(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text, Box } from 'ink';
|
|
3
|
+
// import React from 'react';
|
|
4
|
+
export default function EnhancePrompt() {
|
|
5
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: "Usage" }), _jsx(Text, { children: " $ %NAME%" }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Options" }), _jsx(Text, { children: " --name Your name" }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Examples" }), _jsx(Text, { children: " $ %NAME% --name=Jane" }), _jsx(Text, { children: " Hello, Jane" })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function Help(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// import React from 'react';
|
|
3
|
+
import { Text, Box } from 'ink';
|
|
4
|
+
export default function Help() {
|
|
5
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: "Usage" }), _jsx(Text, { children: " $ %NAME%" }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Options" }), _jsx(Text, { children: " --name Your name" }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Examples" }), _jsx(Text, { children: " $ %NAME% --name=Jane" }), _jsx(Text, { children: " Hello, Jane" })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function DisplayVersion(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import pkg from '../../package.json' with { type: 'json' };
|
|
3
|
+
import { Text, Box } from 'ink';
|
|
4
|
+
export default function DisplayVersion() {
|
|
5
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: pkg.description }), _jsxs(Text, { children: [pkg.name, " v", pkg.version] })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import TextInput from 'ink-text-input';
|
|
5
|
+
import { ConfigManager } from '../lib/config/manager.js';
|
|
6
|
+
import { HistoryManager } from '../lib/history/manager.js';
|
|
7
|
+
import { EnhancementEngine } from '../lib/enhancement/engine.js';
|
|
8
|
+
export default function EnhancePrompt({ onBack: _onBack }) {
|
|
9
|
+
const [state, setState] = useState('input');
|
|
10
|
+
const [prompt, setPrompt] = useState('');
|
|
11
|
+
const [enhancedText, setEnhancedText] = useState('');
|
|
12
|
+
const [error, setError] = useState('');
|
|
13
|
+
const [defaultProvider, setDefaultProvider] = useState('kilo');
|
|
14
|
+
const [loading, setLoading] = useState(true);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
(async () => {
|
|
17
|
+
try {
|
|
18
|
+
const configManager = new ConfigManager();
|
|
19
|
+
await configManager.load();
|
|
20
|
+
const cfg = configManager.getConfig();
|
|
21
|
+
setDefaultProvider(cfg.defaultProvider ?? 'kilo');
|
|
22
|
+
setLoading(false);
|
|
23
|
+
}
|
|
24
|
+
catch (_err) {
|
|
25
|
+
setError('Failed to load configuration');
|
|
26
|
+
setLoading(false);
|
|
27
|
+
}
|
|
28
|
+
})();
|
|
29
|
+
}, []);
|
|
30
|
+
const handleInputComplete = () => {
|
|
31
|
+
if (!prompt.trim()) {
|
|
32
|
+
setError('Prompt cannot be empty');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
setError('');
|
|
36
|
+
void enhancePrompt(defaultProvider);
|
|
37
|
+
};
|
|
38
|
+
const enhancePrompt = async (provider) => {
|
|
39
|
+
setState('enhancing');
|
|
40
|
+
try {
|
|
41
|
+
const configManager = new ConfigManager();
|
|
42
|
+
await configManager.load();
|
|
43
|
+
const historyManager = new HistoryManager();
|
|
44
|
+
await historyManager.load();
|
|
45
|
+
const engine = new EnhancementEngine(configManager, historyManager);
|
|
46
|
+
let result = '';
|
|
47
|
+
const generator = engine.enhanceStream({
|
|
48
|
+
prompt,
|
|
49
|
+
provider: provider,
|
|
50
|
+
saveToHistory: true,
|
|
51
|
+
});
|
|
52
|
+
for await (const chunk of generator) {
|
|
53
|
+
result += chunk;
|
|
54
|
+
setEnhancedText(result);
|
|
55
|
+
}
|
|
56
|
+
if (!result.trim()) {
|
|
57
|
+
throw new Error('Enhancement returned an empty result — the model may be rate-limited or temporarily unavailable. Try a different model.');
|
|
58
|
+
}
|
|
59
|
+
setState('complete');
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
63
|
+
setState('error');
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
if (loading) {
|
|
67
|
+
return (_jsx(Box, { paddingX: 2, children: _jsx(Text, { color: "yellow", children: "Loading configuration..." }) }));
|
|
68
|
+
}
|
|
69
|
+
if (state === 'input') {
|
|
70
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { bold: true, color: "cyan", children: "Enter prompt to enhance:" }), _jsxs(Box, { marginY: 1, children: [_jsx(Text, { children: "Prompt: " }), _jsx(TextInput, { value: prompt, onChange: setPrompt, onSubmit: handleInputComplete, placeholder: "Type your prompt here..." })] }), error && _jsxs(Text, { color: "red", children: ["Error: ", error] })] }));
|
|
71
|
+
}
|
|
72
|
+
if (state === 'enhancing') {
|
|
73
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsxs(Text, { bold: true, color: "cyan", children: ["Enhancing with ", defaultProvider, "..."] }), _jsx(Box, { marginY: 1, children: _jsx(Text, { children: enhancedText }) })] }));
|
|
74
|
+
}
|
|
75
|
+
if (state === 'complete') {
|
|
76
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { bold: true, color: "green", children: "\u2713 Enhancement complete!" }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Original:" }), _jsx(Text, { children: prompt }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Enhanced:" }), _jsx(Text, { children: enhancedText }), _jsx(Text, {}), _jsx(Text, { color: "gray", children: "(Press Ctrl+C to return to menu)" })] }));
|
|
77
|
+
}
|
|
78
|
+
if (state === 'error') {
|
|
79
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { bold: true, color: "red", children: "\u2717 Error:" }), _jsx(Text, { color: "red", children: error }), _jsx(Text, {}), _jsx(Text, { color: "gray", children: "(Press Ctrl+C to return to menu)" })] }));
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import SelectInput from './select-input.js';
|
|
5
|
+
import { HistoryManager } from '../lib/history/manager.js';
|
|
6
|
+
export default function HistoryViewer({ onBack }) {
|
|
7
|
+
const [state, setState] = useState('empty');
|
|
8
|
+
const [entries, setEntries] = useState([]);
|
|
9
|
+
const [selectedEntry, setSelectedEntry] = useState(null);
|
|
10
|
+
const [message, setMessage] = useState('');
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
(async () => {
|
|
13
|
+
try {
|
|
14
|
+
const historyManager = new HistoryManager();
|
|
15
|
+
await historyManager.load();
|
|
16
|
+
const allEntries = historyManager.getEntries();
|
|
17
|
+
if (allEntries.length === 0) {
|
|
18
|
+
setState('empty');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
setEntries(allEntries);
|
|
22
|
+
setState('list');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
setMessage(err instanceof Error ? err.message : String(err));
|
|
27
|
+
setState('error');
|
|
28
|
+
}
|
|
29
|
+
})();
|
|
30
|
+
}, []);
|
|
31
|
+
const handleListSelect = (index) => {
|
|
32
|
+
if (index < entries.length) {
|
|
33
|
+
const entry = entries[index];
|
|
34
|
+
if (entry) {
|
|
35
|
+
setSelectedEntry(entry);
|
|
36
|
+
setState('detail');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if (index === entries.length) {
|
|
40
|
+
// Back option
|
|
41
|
+
onBack();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
if (state === 'empty') {
|
|
45
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { color: "yellow", children: "No enhancement history yet." }), _jsx(Text, { children: "Enhance a prompt to get started!" }), _jsx(Text, {}), _jsx(Text, { color: "gray", children: "(Press Ctrl+C to return to menu)" })] }));
|
|
46
|
+
}
|
|
47
|
+
if (state === 'error') {
|
|
48
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsxs(Text, { color: "red", children: ["Error loading history: ", message] }), _jsx(Text, { color: "gray", children: "(Press Ctrl+C to return to menu)" })] }));
|
|
49
|
+
}
|
|
50
|
+
if (state === 'list') {
|
|
51
|
+
const items = entries.map((entry, idx) => ({
|
|
52
|
+
label: `${new Date(entry.timestamp).toISOString().substring(0, 10)} - ${entry.provider}${entry.originalPrompt.length > 50
|
|
53
|
+
? `: ${entry.originalPrompt.substring(0, 50)}...`
|
|
54
|
+
: `: ${entry.originalPrompt}`}`,
|
|
55
|
+
value: idx,
|
|
56
|
+
}));
|
|
57
|
+
items.push({ label: 'Back', value: entries.length });
|
|
58
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { bold: true, color: "cyan", children: "Enhancement History" }), _jsx(Box, { marginY: 1, children: _jsx(SelectInput, { items: items, onSelect: item => handleListSelect(item.value), initialIndex: 0 }) })] }));
|
|
59
|
+
}
|
|
60
|
+
if (state === 'detail' && selectedEntry) {
|
|
61
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { bold: true, color: "cyan", children: "Enhancement Details" }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Date:" }), _jsx(Text, { children: new Date(selectedEntry.timestamp).toISOString() }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Provider:" }), _jsx(Text, { children: selectedEntry.provider }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Model:" }), _jsx(Text, { children: selectedEntry.model }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Original Prompt:" }), _jsx(Text, { children: selectedEntry.originalPrompt }), _jsx(Text, {}), _jsx(Text, { bold: true, children: "Enhanced Result:" }), _jsx(Text, { children: selectedEntry.enhancedPrompt }), _jsx(Text, {}), _jsx(Box, { marginY: 1, children: _jsx(SelectInput, { items: [{ label: 'Back to List', value: 'back' }], onSelect: () => setState('list'), initialIndex: 0 }) })] }));
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface MenuItem {
|
|
2
|
+
label: string;
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
interface MenuProps {
|
|
6
|
+
title: string;
|
|
7
|
+
items: MenuItem[];
|
|
8
|
+
onSelect: (value: string) => void;
|
|
9
|
+
}
|
|
10
|
+
export default function Menu({ title, items, onSelect }: MenuProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import SelectInput from './select-input.js';
|
|
4
|
+
export default function Menu({ title, items, onSelect }) {
|
|
5
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsx(Text, { bold: true, color: "cyan", children: title }), _jsx(Box, { children: _jsx(SelectInput, { items: items, onSelect: item => onSelect(item.value), initialIndex: 0 }) })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom SelectInput that avoids ink-select-input's handler re-registration
|
|
3
|
+
* bug. The useInput handler has stable identity (empty deps), so ink never
|
|
4
|
+
* tears down / re-adds the listener when selectedIndex changes. Current state
|
|
5
|
+
* is accessed through refs that are updated synchronously on every render.
|
|
6
|
+
*/
|
|
7
|
+
export interface SelectItem<T = string> {
|
|
8
|
+
label: string;
|
|
9
|
+
value: T;
|
|
10
|
+
}
|
|
11
|
+
interface Props<T = string> {
|
|
12
|
+
items: Array<SelectItem<T>>;
|
|
13
|
+
onSelect: (item: SelectItem<T>) => void;
|
|
14
|
+
initialIndex?: number;
|
|
15
|
+
isFocused?: boolean;
|
|
16
|
+
limit?: number;
|
|
17
|
+
}
|
|
18
|
+
export default function SelectInput<T = string>({ items, onSelect, initialIndex, isFocused, limit, }: Props<T>): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Custom SelectInput that avoids ink-select-input's handler re-registration
|
|
4
|
+
* bug. The useInput handler has stable identity (empty deps), so ink never
|
|
5
|
+
* tears down / re-adds the listener when selectedIndex changes. Current state
|
|
6
|
+
* is accessed through refs that are updated synchronously on every render.
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
9
|
+
import { Box, Text, useInput } from 'ink';
|
|
10
|
+
export default function SelectInput({ items, onSelect, initialIndex = 0, isFocused = true, limit, }) {
|
|
11
|
+
const [selectedIndex, setSelectedIndex] = useState(initialIndex);
|
|
12
|
+
// Refs updated synchronously each render — always current inside the stable handler
|
|
13
|
+
const indexRef = useRef(selectedIndex);
|
|
14
|
+
const itemsRef = useRef(items);
|
|
15
|
+
const onSelectRef = useRef(onSelect);
|
|
16
|
+
indexRef.current = selectedIndex;
|
|
17
|
+
itemsRef.current = items;
|
|
18
|
+
onSelectRef.current = onSelect;
|
|
19
|
+
// Reset selection when the items list changes
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
setSelectedIndex(0);
|
|
22
|
+
indexRef.current = 0;
|
|
23
|
+
}, [items]);
|
|
24
|
+
// Stable handler — empty deps means ink registers this ONCE and never
|
|
25
|
+
// tears it down while the component is mounted.
|
|
26
|
+
const handleInput = useCallback((_input, key) => {
|
|
27
|
+
const current = indexRef.current;
|
|
28
|
+
const list = itemsRef.current;
|
|
29
|
+
if (key.upArrow) {
|
|
30
|
+
const next = current > 0 ? current - 1 : list.length - 1;
|
|
31
|
+
indexRef.current = next;
|
|
32
|
+
setSelectedIndex(next);
|
|
33
|
+
}
|
|
34
|
+
else if (key.downArrow) {
|
|
35
|
+
const next = current < list.length - 1 ? current + 1 : 0;
|
|
36
|
+
indexRef.current = next;
|
|
37
|
+
setSelectedIndex(next);
|
|
38
|
+
}
|
|
39
|
+
else if (key.return) {
|
|
40
|
+
const item = list[indexRef.current];
|
|
41
|
+
if (item)
|
|
42
|
+
onSelectRef.current(item);
|
|
43
|
+
}
|
|
44
|
+
}, []); // intentionally empty — accesses all live values via refs
|
|
45
|
+
useInput(handleInput, { isActive: isFocused });
|
|
46
|
+
const displayItems = limit ? items.slice(0, limit) : items;
|
|
47
|
+
return (_jsx(Box, { flexDirection: "column", children: displayItems.map((item, index) => {
|
|
48
|
+
const isSelected = index === selectedIndex;
|
|
49
|
+
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? 'blue' : undefined, children: [isSelected ? '❯ ' : ' ', item.label] }) }, String(item.value)));
|
|
50
|
+
}) }));
|
|
51
|
+
}
|