tuneprompt 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +146 -0
- package/dist/commands/activate.d.ts +1 -0
- package/dist/commands/activate.js +91 -0
- package/dist/commands/fix.d.ts +1 -0
- package/dist/commands/fix.js +187 -0
- package/dist/commands/history.d.ts +5 -0
- package/dist/commands/history.js +63 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +96 -0
- package/dist/commands/run.d.ts +9 -0
- package/dist/commands/run.js +216 -0
- package/dist/db/migrate.d.ts +2 -0
- package/dist/db/migrate.js +8 -0
- package/dist/engine/constraintExtractor.d.ts +8 -0
- package/dist/engine/constraintExtractor.js +54 -0
- package/dist/engine/loader.d.ts +5 -0
- package/dist/engine/loader.js +74 -0
- package/dist/engine/metaPrompt.d.ts +11 -0
- package/dist/engine/metaPrompt.js +129 -0
- package/dist/engine/optimizer.d.ts +26 -0
- package/dist/engine/optimizer.js +246 -0
- package/dist/engine/reporter.d.ts +7 -0
- package/dist/engine/reporter.js +58 -0
- package/dist/engine/runner.d.ts +9 -0
- package/dist/engine/runner.js +169 -0
- package/dist/engine/shadowTester.d.ts +11 -0
- package/dist/engine/shadowTester.js +156 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +26 -0
- package/dist/providers/anthropic.d.ts +12 -0
- package/dist/providers/anthropic.js +51 -0
- package/dist/providers/base.d.ts +15 -0
- package/dist/providers/base.js +10 -0
- package/dist/providers/openai.d.ts +12 -0
- package/dist/providers/openai.js +58 -0
- package/dist/providers/openrouter.d.ts +11 -0
- package/dist/providers/openrouter.js +83 -0
- package/dist/scoring/exact-match.d.ts +1 -0
- package/dist/scoring/exact-match.js +8 -0
- package/dist/scoring/json-validator.d.ts +4 -0
- package/dist/scoring/json-validator.js +29 -0
- package/dist/scoring/semantic.d.ts +8 -0
- package/dist/scoring/semantic.js +107 -0
- package/dist/services/cloud.service.d.ts +49 -0
- package/dist/services/cloud.service.js +82 -0
- package/dist/storage/database.d.ts +10 -0
- package/dist/storage/database.js +179 -0
- package/dist/types/fix.d.ts +28 -0
- package/dist/types/fix.js +2 -0
- package/dist/types/index.d.ts +58 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/analytics.d.ts +2 -0
- package/dist/utils/analytics.js +22 -0
- package/dist/utils/config.d.ts +3 -0
- package/dist/utils/config.js +70 -0
- package/dist/utils/errorHandler.d.ts +14 -0
- package/dist/utils/errorHandler.js +40 -0
- package/dist/utils/license.d.ts +40 -0
- package/dist/utils/license.js +207 -0
- package/dist/utils/storage.d.ts +2 -0
- package/dist/utils/storage.js +25 -0
- package/package.json +76 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CodeForgeNet
|
|
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/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# TunePrompt
|
|
2
|
+
|
|
3
|
+
Industrial-grade testing framework for LLM prompts
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
TunePrompt is a comprehensive testing framework designed specifically for Large Language Model (LLM) prompts. It helps developers validate, test, and optimize their prompts with industrial-grade reliability and accuracy.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Multi-provider Support**: Test prompts across OpenAI, Anthropic, OpenRouter, and other LLM providers
|
|
12
|
+
- **Semantic Testing**: Compare outputs using semantic similarity rather than exact matches
|
|
13
|
+
- **JSON Validation**: Validate structured JSON outputs
|
|
14
|
+
- **LLM-based Judging**: Use advanced LLMs to evaluate prompt quality
|
|
15
|
+
- **Watch Mode**: Automatically re-run tests when files change
|
|
16
|
+
- **CI/CD Integration**: Seamlessly integrate with your CI/CD pipeline
|
|
17
|
+
- **Cloud Sync**: Upload results to the TunePrompt Cloud dashboard
|
|
18
|
+
- **Auto-fix Engine**: Premium feature to automatically fix failing prompts using AI
|
|
19
|
+
- **Detailed Reporting**: Comprehensive test reports with scores, methods, and durations
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g tuneprompt
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
1. Initialize a new project:
|
|
30
|
+
```bash
|
|
31
|
+
tuneprompt init
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
2. Create test files in the `tests` directory with your prompts and expectations
|
|
35
|
+
|
|
36
|
+
3. Run tests:
|
|
37
|
+
```bash
|
|
38
|
+
tuneprompt run
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
4. Run tests with cloud sync (requires activation):
|
|
42
|
+
```bash
|
|
43
|
+
tuneprompt run --cloud
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Commands
|
|
47
|
+
|
|
48
|
+
- `tuneprompt init`: Initialize a new TunePrompt project
|
|
49
|
+
- `tuneprompt run`: Run prompt tests
|
|
50
|
+
- `tuneprompt run --watch`: Run tests in watch mode
|
|
51
|
+
- `tuneprompt run --cloud`: Run tests and upload results to cloud
|
|
52
|
+
- `tuneprompt run --ci`: Run tests in CI mode
|
|
53
|
+
- `tuneprompt fix`: Auto-fix failing prompts (Premium feature)
|
|
54
|
+
- `tuneprompt history`: View test run history
|
|
55
|
+
- `tuneprompt activate [subscription-id]`: Activate your Premium license
|
|
56
|
+
- `tuneprompt status`: Check license status
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
TunePrompt uses a configuration file to define providers and settings. The default location is `tuneprompt.config.js` in your project root.
|
|
61
|
+
|
|
62
|
+
Example configuration:
|
|
63
|
+
```javascript
|
|
64
|
+
module.exports = {
|
|
65
|
+
providers: {
|
|
66
|
+
openai: {
|
|
67
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
68
|
+
model: 'gpt-4o',
|
|
69
|
+
},
|
|
70
|
+
anthropic: {
|
|
71
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
72
|
+
model: 'claude-3-opus-20240229',
|
|
73
|
+
},
|
|
74
|
+
openrouter: {
|
|
75
|
+
apiKey: process.env.OPENROUTER_API_KEY,
|
|
76
|
+
model: 'openai/gpt-4o',
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
threshold: 0.85,
|
|
80
|
+
testDir: './tests',
|
|
81
|
+
outputFormat: 'table'
|
|
82
|
+
};
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Test File Format
|
|
86
|
+
|
|
87
|
+
Tests are defined in JSON files in the `tests` directory. Each test file contains an array of test cases:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
[
|
|
91
|
+
{
|
|
92
|
+
"description": "User onboarding welcome message",
|
|
93
|
+
"prompt": "Generate a friendly welcome message for a user named {{name}}.",
|
|
94
|
+
"variables": {
|
|
95
|
+
"name": "Alice"
|
|
96
|
+
},
|
|
97
|
+
"expect": "Welcome, Alice! We are glad you are here.",
|
|
98
|
+
"config": {
|
|
99
|
+
"threshold": 0.85,
|
|
100
|
+
"method": "semantic",
|
|
101
|
+
"model": "gpt-4o",
|
|
102
|
+
"provider": "openai"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Testing Methods
|
|
109
|
+
|
|
110
|
+
- `exact`: Exact string match
|
|
111
|
+
- `semantic`: Semantic similarity comparison
|
|
112
|
+
- `json`: JSON structure validation
|
|
113
|
+
- `llm-judge`: LLM-based evaluation
|
|
114
|
+
|
|
115
|
+
## Cloud Integration
|
|
116
|
+
|
|
117
|
+
TunePrompt offers cloud synchronization for storing test results and viewing them in a dashboard. To use cloud features:
|
|
118
|
+
|
|
119
|
+
1. Purchase a subscription at [https://tuneprompt.com](https://tuneprompt.com)
|
|
120
|
+
2. Activate your license:
|
|
121
|
+
```bash
|
|
122
|
+
tuneprompt activate [your-subscription-id]
|
|
123
|
+
```
|
|
124
|
+
3. Run tests with cloud sync:
|
|
125
|
+
```bash
|
|
126
|
+
tuneprompt run --cloud
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Premium Features
|
|
130
|
+
|
|
131
|
+
- **Auto-fix Engine**: Automatically repair failing prompts using AI
|
|
132
|
+
- **Cloud sync & team collaboration**: Store results in the cloud and collaborate with your team
|
|
133
|
+
- **Advanced diagnostics**: Detailed insights and recommendations
|
|
134
|
+
|
|
135
|
+
## Environment Variables
|
|
136
|
+
|
|
137
|
+
Create a `.env` file in your project root with your API keys:
|
|
138
|
+
|
|
139
|
+
```env
|
|
140
|
+
OPENAI_API_KEY=your_openai_api_key
|
|
141
|
+
ANTHROPIC_API_KEY=your_anthropic_api_key
|
|
142
|
+
OPENROUTER_API_KEY=your_openrouter_api_key
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Contributing
|
|
146
|
+
|
|
147
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
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 dotenv = __importStar(require("dotenv"));
|
|
43
|
+
const init_1 = require("./commands/init");
|
|
44
|
+
const run_1 = require("./commands/run");
|
|
45
|
+
const history_1 = require("./commands/history");
|
|
46
|
+
const fix_1 = require("./commands/fix");
|
|
47
|
+
const activate_1 = require("./commands/activate");
|
|
48
|
+
// Load environment variables
|
|
49
|
+
dotenv.config();
|
|
50
|
+
const program = new commander_1.Command();
|
|
51
|
+
program
|
|
52
|
+
.name('tuneprompt')
|
|
53
|
+
.description('Industrial-grade testing framework for LLM prompts')
|
|
54
|
+
.version('1.0.0');
|
|
55
|
+
program
|
|
56
|
+
.command('init')
|
|
57
|
+
.description('Initialize a new tuneprompt project')
|
|
58
|
+
.action(async () => {
|
|
59
|
+
await (0, init_1.initCommand)();
|
|
60
|
+
});
|
|
61
|
+
program
|
|
62
|
+
.command('run')
|
|
63
|
+
.description('Run prompt tests')
|
|
64
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
65
|
+
.option('-w, --watch', 'Watch mode for continuous testing')
|
|
66
|
+
.option('--ci', 'CI mode (exit with error code on failure)')
|
|
67
|
+
.option('--cloud', 'Upload results to Tuneprompt Cloud')
|
|
68
|
+
.action(async (options) => {
|
|
69
|
+
if (options.watch) {
|
|
70
|
+
await runWatchMode(options);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
await (0, run_1.runTests)(options);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
program
|
|
77
|
+
.command('fix')
|
|
78
|
+
.description('Auto-fix failing prompts using AI')
|
|
79
|
+
.action(async () => {
|
|
80
|
+
await (0, fix_1.fixCommand)();
|
|
81
|
+
});
|
|
82
|
+
program
|
|
83
|
+
.command('history')
|
|
84
|
+
.description('View test run history')
|
|
85
|
+
.option('-l, --limit <number>', 'Number of runs to show', '10')
|
|
86
|
+
.option('-d, --detailed', 'Show detailed results')
|
|
87
|
+
.action(async (options) => {
|
|
88
|
+
await (0, history_1.historyCommand)({
|
|
89
|
+
limit: parseInt(options.limit),
|
|
90
|
+
detailed: options.detailed
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
program
|
|
94
|
+
.command('activate')
|
|
95
|
+
.argument('[subscription-id]', 'Razorpay subscription ID')
|
|
96
|
+
.description('Activate your Premium license')
|
|
97
|
+
.action(async (subscriptionId) => {
|
|
98
|
+
await (0, activate_1.activateCommand)(subscriptionId);
|
|
99
|
+
});
|
|
100
|
+
program
|
|
101
|
+
.command('status')
|
|
102
|
+
.description('Check license status')
|
|
103
|
+
.action(async () => {
|
|
104
|
+
const { checkLicense, getLicenseInfo } = await Promise.resolve().then(() => __importStar(require('./utils/license')));
|
|
105
|
+
// First verify with backend (this will update DB and delete local license if cancelled)
|
|
106
|
+
const isValid = await checkLicense();
|
|
107
|
+
if (!isValid) {
|
|
108
|
+
console.log(chalk_1.default.yellow('\n⚠️ No active license found\n'));
|
|
109
|
+
console.log(chalk_1.default.gray('You are using the free tier.\n'));
|
|
110
|
+
console.log(chalk_1.default.bold('Upgrade to Premium:'));
|
|
111
|
+
console.log(chalk_1.default.gray(' ' + chalk_1.default.blue.underline('https://tuneprompt.com/pricing') + '\n'));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// If license is valid, show info
|
|
115
|
+
const license = getLicenseInfo();
|
|
116
|
+
if (license) {
|
|
117
|
+
console.log(chalk_1.default.green('\n✅ Premium License Active\n'));
|
|
118
|
+
console.log(chalk_1.default.gray(`Email: ${license.email}`));
|
|
119
|
+
console.log(chalk_1.default.gray(`Plan: ${license.plan}`));
|
|
120
|
+
console.log(chalk_1.default.gray(`Activated: ${new Date(license.activatedAt).toLocaleDateString()}`));
|
|
121
|
+
console.log(chalk_1.default.gray(`Last Verified: ${new Date(license.lastVerified).toLocaleDateString()}\n`));
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Watch mode implementation
|
|
125
|
+
async function runWatchMode(options) {
|
|
126
|
+
const chokidar = require('chokidar');
|
|
127
|
+
console.log(chalk_1.default.cyan('👀 Watch mode enabled. Monitoring for changes...\n'));
|
|
128
|
+
const watcher = chokidar.watch('./tests', {
|
|
129
|
+
persistent: true,
|
|
130
|
+
ignoreInitial: false
|
|
131
|
+
});
|
|
132
|
+
watcher.on('change', async (path) => {
|
|
133
|
+
console.log(chalk_1.default.dim(`\n📝 Detected change in ${path}\n`));
|
|
134
|
+
await (0, run_1.runTests)(options);
|
|
135
|
+
});
|
|
136
|
+
watcher.on('add', async (path) => {
|
|
137
|
+
console.log(chalk_1.default.dim(`\n➕ New test file: ${path}\n`));
|
|
138
|
+
await (0, run_1.runTests)(options);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// Error handling
|
|
142
|
+
process.on('unhandledRejection', (error) => {
|
|
143
|
+
console.error(chalk_1.default.red('\n❌ Unhandled error:'), error.message);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
});
|
|
146
|
+
program.parse();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function activateCommand(subscriptionId?: string): Promise<void>;
|
|
@@ -0,0 +1,91 @@
|
|
|
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.activateCommand = activateCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const license_1 = require("../utils/license");
|
|
11
|
+
async function activateCommand(subscriptionId) {
|
|
12
|
+
console.log(chalk_1.default.bold.cyan('\n🔑 Activate TunePrompt Premium\n'));
|
|
13
|
+
// Check if already activated
|
|
14
|
+
const existingLicense = (0, license_1.getLicenseInfo)();
|
|
15
|
+
if (existingLicense) {
|
|
16
|
+
console.log(chalk_1.default.yellow('⚠️ A license is already activated on this machine.\n'));
|
|
17
|
+
console.log(chalk_1.default.gray(`Email: ${existingLicense.email}`));
|
|
18
|
+
console.log(chalk_1.default.gray(`Plan: ${existingLicense.plan}`));
|
|
19
|
+
console.log(chalk_1.default.gray(`Activated: ${new Date(existingLicense.activatedAt).toLocaleDateString()}\n`));
|
|
20
|
+
const { overwrite } = await inquirer_1.default.prompt([{
|
|
21
|
+
type: 'confirm',
|
|
22
|
+
name: 'overwrite',
|
|
23
|
+
message: 'Do you want to replace it with a new license?',
|
|
24
|
+
default: false
|
|
25
|
+
}]);
|
|
26
|
+
if (!overwrite) {
|
|
27
|
+
console.log(chalk_1.default.gray('\nActivation cancelled.'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Get subscription ID if not provided
|
|
32
|
+
if (!subscriptionId) {
|
|
33
|
+
const answers = await inquirer_1.default.prompt([{
|
|
34
|
+
type: 'input',
|
|
35
|
+
name: 'subscriptionId',
|
|
36
|
+
message: 'Enter your Razorpay Subscription ID:',
|
|
37
|
+
validate: (input) => {
|
|
38
|
+
if (!input || input.trim().length === 0) {
|
|
39
|
+
return 'Subscription ID is required';
|
|
40
|
+
}
|
|
41
|
+
if (!input.startsWith('sub_')) {
|
|
42
|
+
return 'Invalid format. Razorpay Subscription IDs start with "sub_"';
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
}]);
|
|
47
|
+
subscriptionId = answers.subscriptionId;
|
|
48
|
+
}
|
|
49
|
+
const { email } = await inquirer_1.default.prompt([{
|
|
50
|
+
type: 'input',
|
|
51
|
+
name: 'email',
|
|
52
|
+
message: 'Enter your email address:',
|
|
53
|
+
validate: (input) => {
|
|
54
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
55
|
+
return emailRegex.test(input) || 'Please enter a valid email address';
|
|
56
|
+
}
|
|
57
|
+
}]);
|
|
58
|
+
const { plan } = await inquirer_1.default.prompt([{
|
|
59
|
+
type: 'list',
|
|
60
|
+
name: 'plan',
|
|
61
|
+
message: 'Select your plan:',
|
|
62
|
+
choices: [
|
|
63
|
+
{ name: 'Pro Monthly (₹999/month)', value: 'pro-monthly' },
|
|
64
|
+
{ name: 'Pro Yearly (₹9,999/year)', value: 'pro-yearly' },
|
|
65
|
+
{ name: 'Lifetime (₹24,999 one-time)', value: 'lifetime' }
|
|
66
|
+
]
|
|
67
|
+
}]);
|
|
68
|
+
// Activate the license
|
|
69
|
+
const spinner = (0, ora_1.default)('Verifying subscription...').start();
|
|
70
|
+
const success = await (0, license_1.activateLicense)(subscriptionId, email, plan);
|
|
71
|
+
if (success) {
|
|
72
|
+
spinner.succeed('License activated successfully!');
|
|
73
|
+
console.log(chalk_1.default.green('\n✅ TunePrompt Premium is now active!\n'));
|
|
74
|
+
console.log(chalk_1.default.bold('Premium features unlocked:'));
|
|
75
|
+
console.log(chalk_1.default.gray(' • tuneprompt fix (Auto-Fix Engine)'));
|
|
76
|
+
console.log(chalk_1.default.gray(' • Cloud sync & team collaboration'));
|
|
77
|
+
console.log(chalk_1.default.gray(' • Advanced diagnostics\n'));
|
|
78
|
+
console.log(chalk_1.default.cyan('Try running:'), chalk_1.default.bold('tuneprompt fix\n'));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
spinner.fail('Activation failed');
|
|
82
|
+
console.log(chalk_1.default.red('\n❌ Could not activate license\n'));
|
|
83
|
+
console.log(chalk_1.default.yellow('Possible reasons:'));
|
|
84
|
+
console.log(chalk_1.default.gray(' • Invalid subscription ID'));
|
|
85
|
+
console.log(chalk_1.default.gray(' • Subscription is not active'));
|
|
86
|
+
console.log(chalk_1.default.gray(' • Network connection issue\n'));
|
|
87
|
+
console.log(chalk_1.default.bold('Need help?'));
|
|
88
|
+
console.log(chalk_1.default.gray(` • Check your subscription: ${chalk_1.default.blue.underline('https://razorpay.com/my-subscriptions')}`));
|
|
89
|
+
console.log(chalk_1.default.gray(` • Contact support: ${chalk_1.default.blue.underline('support@tuneprompt.com')}\n`));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function fixCommand(): Promise<void>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.fixCommand = fixCommand;
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
+
const ora_1 = __importDefault(require("ora"));
|
|
42
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
43
|
+
const optimizer_1 = require("../engine/optimizer");
|
|
44
|
+
const license_1 = require("../utils/license");
|
|
45
|
+
const analytics_1 = require("../utils/analytics");
|
|
46
|
+
const storage_1 = require("../utils/storage");
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const errorHandler_1 = require("../utils/errorHandler");
|
|
49
|
+
async function fixCommand() {
|
|
50
|
+
try {
|
|
51
|
+
console.log(chalk_1.default.bold.cyan('\n🔧 TunePrompt Fix\n'));
|
|
52
|
+
// License check with better error
|
|
53
|
+
const spinner = (0, ora_1.default)('Checking license...').start();
|
|
54
|
+
const licenseValid = await (0, license_1.checkLicense)();
|
|
55
|
+
if (!licenseValid) {
|
|
56
|
+
spinner.fail('Premium feature locked');
|
|
57
|
+
showUpgradePrompt();
|
|
58
|
+
throw errorHandler_1.Errors.NO_LICENSE;
|
|
59
|
+
}
|
|
60
|
+
spinner.succeed('License validated');
|
|
61
|
+
// Load failed tests with error handling
|
|
62
|
+
const failedTests = await (0, storage_1.getFailedTests)();
|
|
63
|
+
if (failedTests.length === 0) {
|
|
64
|
+
throw errorHandler_1.Errors.NO_FAILED_TESTS;
|
|
65
|
+
}
|
|
66
|
+
console.log(chalk_1.default.yellow(`\nFound ${failedTests.length} failed test(s):\n`));
|
|
67
|
+
failedTests.forEach((test, index) => {
|
|
68
|
+
console.log(`${index + 1}. ${chalk_1.default.bold(test.description)}`);
|
|
69
|
+
console.log(` Score: ${chalk_1.default.red(test.score.toFixed(2))} (threshold: ${test.threshold})`);
|
|
70
|
+
});
|
|
71
|
+
// Step 3: Ask which tests to fix
|
|
72
|
+
const { selectedIndexes } = await inquirer_1.default.prompt([{
|
|
73
|
+
type: 'checkbox',
|
|
74
|
+
name: 'selectedIndexes',
|
|
75
|
+
message: 'Which tests would you like to fix?',
|
|
76
|
+
choices: failedTests.map((test, index) => ({
|
|
77
|
+
name: `${test.description} (score: ${test.score.toFixed(2)})`,
|
|
78
|
+
value: index,
|
|
79
|
+
checked: true
|
|
80
|
+
}))
|
|
81
|
+
}]);
|
|
82
|
+
if (selectedIndexes.length === 0) {
|
|
83
|
+
console.log(chalk_1.default.gray('\nNo tests selected. Exiting.'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// Step 4: Optimize each selected test
|
|
87
|
+
const optimizer = new optimizer_1.PromptOptimizer();
|
|
88
|
+
for (const index of selectedIndexes) {
|
|
89
|
+
const test = failedTests[index];
|
|
90
|
+
console.log(chalk_1.default.bold(`\n\n━━━ Fixing: ${test.description} ━━━\n`));
|
|
91
|
+
try {
|
|
92
|
+
const result = await optimizer.optimize(test);
|
|
93
|
+
await showDiff(result.originalPrompt, result.optimizedPrompt, result.reasoning);
|
|
94
|
+
// Ask if user wants to apply
|
|
95
|
+
const { action } = await inquirer_1.default.prompt([{
|
|
96
|
+
type: 'rawlist',
|
|
97
|
+
name: 'action',
|
|
98
|
+
message: 'What would you like to do?',
|
|
99
|
+
choices: [
|
|
100
|
+
{ name: 'Apply this fix (Updates your test file)', value: 'apply' },
|
|
101
|
+
{ name: 'Edit before applying', value: 'edit' },
|
|
102
|
+
{ name: 'Skip this fix', value: 'skip' }
|
|
103
|
+
],
|
|
104
|
+
default: 0
|
|
105
|
+
}]);
|
|
106
|
+
if (action === 'apply') {
|
|
107
|
+
await applyFix(test, result.optimizedPrompt);
|
|
108
|
+
console.log(`\n${chalk_1.default.bgGreen.black(' DONE ')} ${chalk_1.default.green('Prompt updated in:')} ${chalk_1.default.bold(test.id)}`);
|
|
109
|
+
console.log(chalk_1.default.gray('The next run will use this new prompt.\n'));
|
|
110
|
+
}
|
|
111
|
+
else if (action === 'edit') {
|
|
112
|
+
console.log(chalk_1.default.gray('\nOpening editor... (Save and close to apply)\n'));
|
|
113
|
+
const { edited } = await inquirer_1.default.prompt([{
|
|
114
|
+
type: 'editor',
|
|
115
|
+
name: 'edited',
|
|
116
|
+
message: 'Edit the prompt (save and close when done):',
|
|
117
|
+
default: result.optimizedPrompt
|
|
118
|
+
}]);
|
|
119
|
+
await applyFix(test, edited);
|
|
120
|
+
console.log(chalk_1.default.green('✅ Edited fix applied!'));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log(chalk_1.default.gray('⏭️ Skipped'));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error(chalk_1.default.red(`\n❌ Failed to optimize "${test.description}"`));
|
|
128
|
+
console.error(chalk_1.default.gray(`Reason: ${error.message}\n`));
|
|
129
|
+
continue; // Skip to next test
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
console.log(chalk_1.default.bold.green('\n\n✨ Fix session complete!\n'));
|
|
133
|
+
console.log(chalk_1.default.gray('Run `tuneprompt run` to verify your fixes.\n'));
|
|
134
|
+
// After fix completes
|
|
135
|
+
const license = (0, license_1.getLicenseInfo)();
|
|
136
|
+
if (license) {
|
|
137
|
+
(0, analytics_1.trackFixAttempt)(license.email, selectedIndexes.length, true);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
(0, errorHandler_1.handleError)(error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function showUpgradePrompt() {
|
|
145
|
+
console.log(chalk_1.default.yellow('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
146
|
+
console.log(chalk_1.default.bold('🔒 Premium Feature: Auto-Fix Engine\n'));
|
|
147
|
+
console.log('The ' + chalk_1.default.cyan('fix') + ' command uses advanced AI to automatically');
|
|
148
|
+
console.log('repair your failing prompts.\n');
|
|
149
|
+
console.log(chalk_1.default.bold('What you get:'));
|
|
150
|
+
console.log(' ✅ AI-powered prompt optimization');
|
|
151
|
+
console.log(' ✅ Shadow testing before applying fixes');
|
|
152
|
+
console.log(' ✅ Interactive diff viewer');
|
|
153
|
+
console.log(' ✅ Unlimited fix attempts\n');
|
|
154
|
+
console.log(chalk_1.default.bold('Get Premium:'));
|
|
155
|
+
console.log(` 1. Buy a license: ${chalk_1.default.blue.underline('https://tuneprompt.com/pricing')}`);
|
|
156
|
+
console.log(` 2. Activate: ${chalk_1.default.gray('tuneprompt activate <your-key>')}\n`);
|
|
157
|
+
console.log(chalk_1.default.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
158
|
+
}
|
|
159
|
+
async function showDiff(original, optimized, reasoning) {
|
|
160
|
+
const diffLib = await Promise.resolve().then(() => __importStar(require('diff')));
|
|
161
|
+
console.log(chalk_1.default.bold('\n💡 AI Analysis:'));
|
|
162
|
+
console.log(chalk_1.default.gray(reasoning));
|
|
163
|
+
console.log(chalk_1.default.bold('\n📝 Proposed Changes:\n'));
|
|
164
|
+
const diff = diffLib.diffWords(original, optimized);
|
|
165
|
+
diff.forEach((part) => {
|
|
166
|
+
const color = part.added ? chalk_1.default.green :
|
|
167
|
+
part.removed ? chalk_1.default.red :
|
|
168
|
+
chalk_1.default.gray;
|
|
169
|
+
const prefix = part.added ? '+ ' :
|
|
170
|
+
part.removed ? '- ' :
|
|
171
|
+
' ';
|
|
172
|
+
process.stdout.write(color(prefix + part.value));
|
|
173
|
+
});
|
|
174
|
+
console.log('\n');
|
|
175
|
+
}
|
|
176
|
+
async function applyFix(test, newPrompt) {
|
|
177
|
+
// This assumes prompts are stored in JSON test files
|
|
178
|
+
// Adjust based on your Phase 1 implementation
|
|
179
|
+
const testFilePath = test.id; // Assuming ID stores file path
|
|
180
|
+
if (!fs.existsSync(testFilePath)) {
|
|
181
|
+
console.error(chalk_1.default.red(`Test file not found: ${testFilePath}`));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const testData = JSON.parse(fs.readFileSync(testFilePath, 'utf-8'));
|
|
185
|
+
testData.prompt = newPrompt;
|
|
186
|
+
fs.writeFileSync(testFilePath, JSON.stringify(testData, null, 2));
|
|
187
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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.historyCommand = historyCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
9
|
+
const database_1 = require("../storage/database");
|
|
10
|
+
async function historyCommand(options) {
|
|
11
|
+
const db = new database_1.TestDatabase();
|
|
12
|
+
const runs = db.getRecentRuns(options.limit || 10);
|
|
13
|
+
db.close();
|
|
14
|
+
if (runs.length === 0) {
|
|
15
|
+
console.log(chalk_1.default.yellow('No test history found. Run some tests first!'));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
console.log(chalk_1.default.bold('\n📊 Test History\n'));
|
|
19
|
+
const table = new cli_table3_1.default({
|
|
20
|
+
head: ['Date', 'Total', 'Passed', 'Failed', 'Duration', 'Pass Rate'],
|
|
21
|
+
colWidths: [20, 8, 10, 10, 12, 12]
|
|
22
|
+
});
|
|
23
|
+
for (const run of runs) {
|
|
24
|
+
const passRate = ((run.passed / run.totalTests) * 100).toFixed(1);
|
|
25
|
+
const passRateColor = parseFloat(passRate) >= 80
|
|
26
|
+
? chalk_1.default.green
|
|
27
|
+
: parseFloat(passRate) >= 50
|
|
28
|
+
? chalk_1.default.yellow
|
|
29
|
+
: chalk_1.default.red;
|
|
30
|
+
table.push([
|
|
31
|
+
run.timestamp.toLocaleString(),
|
|
32
|
+
run.totalTests,
|
|
33
|
+
chalk_1.default.green(run.passed),
|
|
34
|
+
chalk_1.default.red(run.failed),
|
|
35
|
+
`${run.duration}ms`,
|
|
36
|
+
passRateColor(`${passRate}%`)
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
console.log(table.toString());
|
|
40
|
+
if (options.detailed) {
|
|
41
|
+
console.log(chalk_1.default.bold('\n📝 Detailed Results\n'));
|
|
42
|
+
for (const run of runs) {
|
|
43
|
+
console.log(chalk_1.default.dim(`\nRun: ${run.id}`));
|
|
44
|
+
console.log(chalk_1.default.dim(`Date: ${run.timestamp.toLocaleString()}\n`));
|
|
45
|
+
const detailTable = new cli_table3_1.default({
|
|
46
|
+
head: ['Status', 'Test', 'Score'],
|
|
47
|
+
colWidths: [10, 50, 10]
|
|
48
|
+
});
|
|
49
|
+
for (const result of run.results) {
|
|
50
|
+
const status = result.status === 'pass'
|
|
51
|
+
? chalk_1.default.green('✓')
|
|
52
|
+
: chalk_1.default.red('✗');
|
|
53
|
+
detailTable.push([
|
|
54
|
+
status,
|
|
55
|
+
result.testCase.description,
|
|
56
|
+
result.score.toFixed(2)
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
console.log(detailTable.toString());
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initCommand(): Promise<void>;
|