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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +151 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +146 -0
  5. package/dist/commands/activate.d.ts +1 -0
  6. package/dist/commands/activate.js +91 -0
  7. package/dist/commands/fix.d.ts +1 -0
  8. package/dist/commands/fix.js +187 -0
  9. package/dist/commands/history.d.ts +5 -0
  10. package/dist/commands/history.js +63 -0
  11. package/dist/commands/init.d.ts +1 -0
  12. package/dist/commands/init.js +96 -0
  13. package/dist/commands/run.d.ts +9 -0
  14. package/dist/commands/run.js +216 -0
  15. package/dist/db/migrate.d.ts +2 -0
  16. package/dist/db/migrate.js +8 -0
  17. package/dist/engine/constraintExtractor.d.ts +8 -0
  18. package/dist/engine/constraintExtractor.js +54 -0
  19. package/dist/engine/loader.d.ts +5 -0
  20. package/dist/engine/loader.js +74 -0
  21. package/dist/engine/metaPrompt.d.ts +11 -0
  22. package/dist/engine/metaPrompt.js +129 -0
  23. package/dist/engine/optimizer.d.ts +26 -0
  24. package/dist/engine/optimizer.js +246 -0
  25. package/dist/engine/reporter.d.ts +7 -0
  26. package/dist/engine/reporter.js +58 -0
  27. package/dist/engine/runner.d.ts +9 -0
  28. package/dist/engine/runner.js +169 -0
  29. package/dist/engine/shadowTester.d.ts +11 -0
  30. package/dist/engine/shadowTester.js +156 -0
  31. package/dist/index.d.ts +7 -0
  32. package/dist/index.js +26 -0
  33. package/dist/providers/anthropic.d.ts +12 -0
  34. package/dist/providers/anthropic.js +51 -0
  35. package/dist/providers/base.d.ts +15 -0
  36. package/dist/providers/base.js +10 -0
  37. package/dist/providers/openai.d.ts +12 -0
  38. package/dist/providers/openai.js +58 -0
  39. package/dist/providers/openrouter.d.ts +11 -0
  40. package/dist/providers/openrouter.js +83 -0
  41. package/dist/scoring/exact-match.d.ts +1 -0
  42. package/dist/scoring/exact-match.js +8 -0
  43. package/dist/scoring/json-validator.d.ts +4 -0
  44. package/dist/scoring/json-validator.js +29 -0
  45. package/dist/scoring/semantic.d.ts +8 -0
  46. package/dist/scoring/semantic.js +107 -0
  47. package/dist/services/cloud.service.d.ts +49 -0
  48. package/dist/services/cloud.service.js +82 -0
  49. package/dist/storage/database.d.ts +10 -0
  50. package/dist/storage/database.js +179 -0
  51. package/dist/types/fix.d.ts +28 -0
  52. package/dist/types/fix.js +2 -0
  53. package/dist/types/index.d.ts +58 -0
  54. package/dist/types/index.js +2 -0
  55. package/dist/utils/analytics.d.ts +2 -0
  56. package/dist/utils/analytics.js +22 -0
  57. package/dist/utils/config.d.ts +3 -0
  58. package/dist/utils/config.js +70 -0
  59. package/dist/utils/errorHandler.d.ts +14 -0
  60. package/dist/utils/errorHandler.js +40 -0
  61. package/dist/utils/license.d.ts +40 -0
  62. package/dist/utils/license.js +207 -0
  63. package/dist/utils/storage.d.ts +2 -0
  64. package/dist/utils/storage.js +25 -0
  65. 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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
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,5 @@
1
+ export interface HistoryOptions {
2
+ limit?: number;
3
+ detailed?: boolean;
4
+ }
5
+ export declare function historyCommand(options: HistoryOptions): Promise<void>;
@@ -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>;