diff --git a/messages/en.json b/messages/en.json index b71bf370..e15638e5 100644 --- a/messages/en.json +++ b/messages/en.json @@ -44,7 +44,8 @@ "profile": "Profile", "login": "Login", "register": "Register", - "logout": "Logout" + "logout": "Logout", + "ide": "Typed Prompts" }, "brand": { "copyLogoSvg": "Copy Logo SVG", @@ -1112,5 +1113,15 @@ "contentConstraints": "Content constraints:", "contentExample": "\"Do not include personal opinions\" / \"Focus only on factual information\"" } + }, + "ide": { + "title": "Typed Prompts", + "subtitle": "Type-safe, structured prompt engineering", + "editor": "Editor", + "preview": "Preview", + "run": "Run", + "reset": "Reset", + "copied": "Copied to clipboard", + "runToPreview": "Click Run to see the prompt preview" } } diff --git a/package-lock.json b/package-lock.json index 1a45c309..a951a64f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,6 +45,7 @@ "next-themes": "^0.4.6", "openai": "^6.10.0", "pino": "^10.1.0", + "prompts.chat": "file:./packages/prompts.chat", "react": "19.2.0", "react-dom": "19.2.0", "react-hook-form": "^7.68.0", @@ -1258,7 +1259,6 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.5.0.tgz", "integrity": "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==", - "dev": true, "license": "MIT", "dependencies": { "picocolors": "^1.0.0", @@ -1269,7 +1269,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.11.0.tgz", "integrity": "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==", - "dev": true, "license": "MIT", "dependencies": { "@clack/core": "0.5.0", @@ -4568,6 +4567,314 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz", + "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz", + "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", + "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz", + "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz", + "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz", + "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz", + "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz", + "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz", + "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz", + "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz", + "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz", + "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz", + "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz", + "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz", + "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz", + "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz", + "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz", + "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz", + "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz", + "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz", + "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz", + "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -5799,6 +6106,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/d3": { "version": "7.4.3", "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", @@ -6061,6 +6379,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -6707,6 +7032,117 @@ "win32" ] }, + "node_modules/@vitest/expect": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.16.tgz", + "integrity": "sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.16", + "@vitest/utils": "4.0.16", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.16.tgz", + "integrity": "sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.16", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.16.tgz", + "integrity": "sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.16.tgz", + "integrity": "sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.16", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.16.tgz", + "integrity": "sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.16", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.16.tgz", + "integrity": "sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.16.tgz", + "integrity": "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.16", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -6792,6 +7228,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -6981,6 +7424,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -7175,6 +7628,22 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -7213,6 +7682,16 @@ } } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -7301,6 +7780,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8462,6 +8951,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -9040,6 +9536,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -9080,6 +9586,16 @@ "node": ">=18.0.0" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -9331,6 +9847,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -10447,6 +10975,16 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -10840,6 +11378,36 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -11624,6 +12192,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, "node_modules/monaco-editor": { "version": "0.55.1", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", @@ -11641,6 +12241,18 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -12513,6 +13125,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/ohash": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", @@ -12791,6 +13414,16 @@ "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", "license": "MIT" }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/pkce-challenge": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", @@ -12857,6 +13490,49 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/preact": { "version": "10.24.3", "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", @@ -12928,6 +13604,10 @@ ], "license": "MIT" }, + "node_modules/prompts.chat": { + "resolved": "packages/prompts.chat", + "link": true + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -13374,6 +14054,48 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", "license": "Unlicense" }, + "node_modules/rollup": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", + "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.54.0", + "@rollup/rollup-android-arm64": "4.54.0", + "@rollup/rollup-darwin-arm64": "4.54.0", + "@rollup/rollup-darwin-x64": "4.54.0", + "@rollup/rollup-freebsd-arm64": "4.54.0", + "@rollup/rollup-freebsd-x64": "4.54.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", + "@rollup/rollup-linux-arm-musleabihf": "4.54.0", + "@rollup/rollup-linux-arm64-gnu": "4.54.0", + "@rollup/rollup-linux-arm64-musl": "4.54.0", + "@rollup/rollup-linux-loong64-gnu": "4.54.0", + "@rollup/rollup-linux-ppc64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-musl": "4.54.0", + "@rollup/rollup-linux-s390x-gnu": "4.54.0", + "@rollup/rollup-linux-x64-gnu": "4.54.0", + "@rollup/rollup-linux-x64-musl": "4.54.0", + "@rollup/rollup-openharmony-arm64": "4.54.0", + "@rollup/rollup-win32-arm64-msvc": "4.54.0", + "@rollup/rollup-win32-ia32-msvc": "4.54.0", + "@rollup/rollup-win32-x64-gnu": "4.54.0", + "@rollup/rollup-win32-x64-msvc": "4.54.0", + "fsevents": "~2.3.2" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -13750,6 +14472,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/simple-swizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", @@ -13763,7 +14492,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, "license": "MIT" }, "node_modules/sonic-boom": { @@ -13785,6 +14513,16 @@ "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -13820,6 +14558,13 @@ "dev": true, "license": "MIT" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/state-local": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", @@ -13835,6 +14580,13 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -14052,6 +14804,39 @@ } } }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -14109,6 +14894,29 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/thread-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", @@ -14118,6 +14926,13 @@ "real-require": "^0.2.0" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyexec": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", @@ -14176,6 +14991,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -14197,6 +15022,16 @@ "node": ">=0.6" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -14230,6 +15065,13 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -14435,6 +15277,13 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -14736,6 +15585,203 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vite": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz", + "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.16.tgz", + "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.16", + "@vitest/mocker": "4.0.16", + "@vitest/pretty-format": "4.0.16", + "@vitest/runner": "4.0.16", + "@vitest/snapshot": "4.0.16", + "@vitest/spy": "4.0.16", + "@vitest/utils": "4.0.16", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.16", + "@vitest/browser-preview": "4.0.16", + "@vitest/browser-webdriverio": "4.0.16", + "@vitest/ui": "4.0.16", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -14840,6 +15886,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -14931,6 +15994,93 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } + }, + "packages/prompts.chat": { + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "@clack/prompts": "^0.11.0", + "picocolors": "^1.1.1" + }, + "devDependencies": { + "@types/node": "^20", + "tsup": "^8.0.0", + "typescript": "^5", + "vitest": "^4.0.16" + }, + "engines": { + "node": ">=18" + } + }, + "packages/prompts.chat/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "packages/prompts.chat/node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "packages/prompts.chat/node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 7b2a6011..e4b94119 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "postinstall": "prisma generate" }, "dependencies": { + "prompts.chat": "file:./packages/prompts.chat", "@auth/prisma-adapter": "^2.11.1", "@aws-sdk/client-s3": "^3.948.0", "@hookform/resolvers": "^5.2.2", diff --git a/packages/prompts.chat/.gitignore b/packages/prompts.chat/.gitignore new file mode 100644 index 00000000..dd6e803c --- /dev/null +++ b/packages/prompts.chat/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +*.log +.DS_Store diff --git a/packages/prompts.chat/README.md b/packages/prompts.chat/README.md new file mode 100644 index 00000000..34d964c1 --- /dev/null +++ b/packages/prompts.chat/README.md @@ -0,0 +1,2344 @@ +# prompts.chat + +The **D3.js of prompt engineering** — a comprehensive developer toolkit for building, validating, and parsing AI prompts. Create structured prompts for chat models, image generators, video AI, and music generation with fluent, type-safe APIs. + +## Installation + +```bash +npm install prompts.chat +``` + +## Quick Start + +```typescript +import { builder, chat, image, audio, variables, quality } from 'prompts.chat'; + +// Build structured text prompts +const prompt = builder() + .role("Senior TypeScript Developer") + .task("Review the following code for bugs and improvements") + .constraints(["Be concise", "Focus on critical issues"]) + .variable("code", { required: true }) + .build(); + +// Build chat prompts with full control +const chatPrompt = chat() + .role("expert code reviewer") + .tone("professional") + .expertise("TypeScript", "React", "Node.js") + .task("Review code and provide actionable feedback") + .stepByStep() + .json() + .build(); + +// Build image generation prompts +const imagePrompt = image() + .subject("a cyberpunk samurai") + .environment("neon-lit Tokyo streets") + .shot("medium") + .lens("35mm") + .lightingType("rim") + .medium("cinematic") + .build(); + +// Build music generation prompts +const musicPrompt = audio() + .genre("electronic") + .mood("energetic") + .bpm(128) + .instruments(["synthesizer", "drums", "bass"]) + .build(); + +// Normalize variable formats +const normalized = variables.normalize("Hello {{name}}, you are [ROLE]"); +// → "Hello ${name}, you are ${role}" + +// Check quality locally (no API needed) +const result = quality.check("Act as a developer..."); +console.log(result.score); // 0.85 +``` + +--- + +## Modules + +- [Variables](#-variables) — Variable detection and normalization +- [Similarity](#-similarity) — Content deduplication +- [Builder](#️-builder) — Structured text prompts +- [Chat Builder](#-chat-builder) — Chat/conversational prompts +- [Image Builder](#-image-builder) — Image generation prompts +- [Video Builder](#-video-builder) — Video generation prompts +- [Audio Builder](#-audio-builder) — Music/audio generation prompts +- [Quality](#-quality) — Prompt validation +- [Parser](#-parser) — Multi-format parsing + +--- + +## 🔧 Variables + +Universal variable detection and normalization across different formats. + +```typescript +import { variables } from 'prompts.chat'; + +// Detect variables in any format +const detected = variables.detect("Hello {{name}}, welcome to [COMPANY]"); +// → [{ name: "name", pattern: "double_curly" }, { name: "COMPANY", pattern: "single_bracket" }] + +// Normalize all formats to ${var} +const normalized = variables.normalize("Hello {{name}}, you are [ROLE]"); +// → "Hello ${name}, you are ${role}" + +// Extract variables from ${var} format +const vars = variables.extractVariables("Hello ${name:World}"); +// → [{ name: "name", defaultValue: "World" }] + +// Compile template with values +const result = variables.compile("Hello ${name:World}", { name: "Developer" }); +// → "Hello Developer" + +// Get pattern descriptions +variables.getPatternDescription("double_bracket"); // → "[[...]]" +``` + +### Supported Formats + +| Format | Example | Pattern Name | +|--------|---------|--------------| +| `${var}` | `${name}` | `dollar_curly` | +| `${var:default}` | `${name:World}` | `dollar_curly` | +| `{{var}}` | `{{name}}` | `double_curly` | +| `[[var]]` | `[[name]]` | `double_bracket` | +| `[VAR]` | `[NAME]` | `single_bracket` | +| `{VAR}` | `{NAME}` | `single_curly` | +| `` | `` | `angle_bracket` | +| `%VAR%` | `%NAME%` | `percent` | + +### API Reference + +| Function | Description | +|----------|-------------| +| `detect(text)` | Detect variables in any format | +| `normalize(text)` | Convert all formats to `${var}` | +| `extractVariables(text)` | Extract from `${var}` format | +| `compile(text, values, options?)` | Replace variables with values | +| `convertToSupportedFormat(variable)` | Convert a single variable | +| `getPatternDescription(pattern)` | Get human-readable pattern | + +--- + +## 📊 Similarity + +Content similarity detection for deduplication using Jaccard and n-gram algorithms. + +```typescript +import { similarity } from 'prompts.chat'; + +// Calculate similarity score (0-1) +const score = similarity.calculate(prompt1, prompt2); +// → 0.87 + +// Check if prompts are duplicates (default threshold: 0.85) +const isDupe = similarity.isDuplicate(prompt1, prompt2, 0.85); +// → true + +// Find groups of duplicate prompts +const groups = similarity.findDuplicates(prompts, 0.85); +// → [[prompt1, prompt3], [prompt2, prompt5]] + +// Deduplicate an array (keeps first occurrence) +const unique = similarity.deduplicate(prompts, 0.85); + +// Get content fingerprint for indexing +const fingerprint = similarity.getContentFingerprint(prompt); + +// Normalize content for comparison +const normalized = similarity.normalizeContent(text); +``` + +### API Reference + +| Function | Description | +|----------|-------------| +| `calculate(content1, content2)` | Calculate similarity score (0-1) | +| `isDuplicate(content1, content2, threshold?)` | Check if similar (default 0.85) | +| `findDuplicates(prompts, threshold?)` | Find groups of duplicates | +| `deduplicate(prompts, threshold?)` | Remove duplicates | +| `normalizeContent(content)` | Normalize for comparison | +| `getContentFingerprint(content)` | Get fingerprint for indexing | + +--- + +## 🏗️ Builder + +Fluent DSL for creating structured text prompts. + +```typescript +import { builder, fromPrompt, templates } from 'prompts.chat'; + +// Build a custom prompt +const prompt = builder() + .role("Senior Developer") + .context("You are helping review a React application") + .task("Analyze the code for performance issues") + .constraints([ + "Be concise", + "Focus on critical issues", + "Suggest fixes with code examples" + ]) + .output("JSON with { issues: [], suggestions: [] }") + .variable("code", { required: true, description: "Code to review" }) + .example("const x = 1;", '{ "issues": [], "suggestions": [] }') + .section("Additional Notes", "Consider React 18 best practices") + .build(); + +console.log(prompt.content); +console.log(prompt.variables); +console.log(prompt.metadata); + +// Create from existing prompt +const existing = fromPrompt("You are a helpful assistant...").build(); +``` + +### Builder Methods + +| Method | Description | +|--------|-------------| +| `.role(role)` | Set AI persona (alias: `.persona()`) | +| `.context(context)` | Set background info (alias: `.background()`) | +| `.task(task)` | Set main instruction (alias: `.instruction()`) | +| `.constraints(list)` | Add multiple constraints (alias: `.rules()`) | +| `.constraint(text)` | Add single constraint | +| `.output(format)` | Set output format (alias: `.format()`) | +| `.example(input, output)` | Add input/output example | +| `.examples(list)` | Add multiple examples | +| `.variable(name, options?)` | Define a variable | +| `.section(title, content)` | Add custom section | +| `.raw(content)` | Set raw content | +| `.build()` | Build the prompt | +| `.toString()` | Get content string | + +### Pre-built Templates + +```typescript +import { templates } from 'prompts.chat'; + +// Code review template +const review = templates.codeReview({ + language: "TypeScript", + focus: ["performance", "security"] +}); + +// Translation template +const translate = templates.translation("English", "Spanish"); + +// Summarization template +const summary = templates.summarize({ + maxLength: 100, + style: "bullet" +}); + +// Q&A template +const qa = templates.qa("You are answering questions about React."); +``` + +--- + +## 💬 Chat Builder + +Comprehensive model-agnostic builder for conversational AI prompts. Works with GPT-4, Claude, Gemini, Llama, and any chat model. + +### Quick Start + +```typescript +import { chat } from 'prompts.chat'; + +const prompt = chat() + .role("helpful coding assistant") + .context("Building a React application") + .task("Explain async/await in JavaScript") + .stepByStep() + .detailed() + .build(); + +console.log(prompt.systemPrompt); +console.log(prompt.messages); +``` + +### Full Example + +```typescript +import { chat } from 'prompts.chat'; + +const prompt = chat() + // ━━━ Persona ━━━ + .persona({ + name: "Alex", + role: "senior software architect", + tone: ["professional", "analytical"], + expertise: ["system-design", "microservices", "cloud-architecture"], + personality: ["patient", "thorough", "pragmatic"], + background: "15 years of experience at FAANG companies", + language: "English", + verbosity: "detailed" + }) + .role("expert code reviewer") // Override role + .tone(["technical", "concise"]) // Override tone(s) + .expertise(["coding", "engineering"]) // Override expertise + .personality(["direct", "helpful"]) // Override personality + .background("Specialized in distributed systems") + .speakAs("CodeReviewer") // Set a name + .responseLanguage("English") // Response language + + // ━━━ Context ━━━ + .context({ + background: "Reviewing a pull request for an e-commerce platform", + domain: "software engineering", + audience: "mid-level developers", + purpose: "improve code quality and maintainability", + constraints: ["Follow team coding standards", "Consider performance"], + assumptions: ["Team uses TypeScript", "Project uses React"], + knowledge: ["Existing codebase uses Redux", "Team prefers functional components"] + }) + .domain("web development") // Set knowledge domain + .audience("junior developers") // Target audience + .purpose("educational code review") // Purpose of interaction + .constraints(["Be constructive", "Explain why"]) // Add constraints + .constraint("Focus on security issues") // Single constraint + .assumptions(["Code compiles successfully"]) // Set assumptions + .knowledge(["Using React 18", "Node.js backend"]) // Known facts + + // ━━━ Task ━━━ + .task({ + instruction: "Review the submitted code and provide actionable feedback", + steps: [ + "Analyze code structure and organization", + "Check for potential bugs and edge cases", + "Evaluate performance implications", + "Suggest improvements with examples" + ], + deliverables: ["Summary of issues", "Prioritized recommendations", "Code examples"], + criteria: ["Feedback is specific", "Examples are provided", "Tone is constructive"], + antiPatterns: ["Vague criticism", "No examples", "Harsh language"], + priority: "accuracy" + }) + .instruction("Review this React component") // Main instruction + .steps([ // Override steps + "Check hooks usage", + "Verify prop types", + "Review state management" + ]) + .deliverables(["Issue list", "Fixed code"]) // Expected deliverables + .criteria(["Clear explanations"]) // Success criteria + .avoid(["Being overly critical", "Ignoring context"]) // Anti-patterns + .priority("thoroughness") // accuracy | speed | creativity | thoroughness + + // ━━━ Examples (Few-Shot) ━━━ + .example( + "const [data, setData] = useState()", + "Consider adding a type parameter: useState()", + "TypeScript generics improve type safety" + ) + .examples([ + { input: "useEffect(() => { fetch() })", output: "Add cleanup function for async operations" }, + { input: "if(data == null)", output: "Use strict equality (===) for null checks" } + ]) + .fewShot([ // Alternative few-shot syntax + { input: "var x = 1", output: "Use const or let instead of var" }, + { input: "any[]", output: "Define specific array types" } + ]) + + // ━━━ Output Format ━━━ + .output({ + format: { type: "markdown" }, + length: "detailed", + style: "mixed", + includeExamples: true, + includeExplanation: true + }) + .outputFormat("markdown") // text | json | markdown | code | table + .json() // Shortcut for JSON output + .jsonSchema("CodeReview", { // JSON with schema + type: "object", + properties: { + issues: { type: "array" }, + suggestions: { type: "array" } + } + }) + .markdown() // Markdown output + .code("typescript") // Code output with language + .table() // Table output + + // ━━━ Output Length ━━━ + .length("detailed") // brief | moderate | detailed | comprehensive | exhaustive + .brief() // Shortcut for brief + .moderate() // Shortcut for moderate + .detailed() // Shortcut for detailed + .comprehensive() // Shortcut for comprehensive + .exhaustive() // Shortcut for exhaustive + + // ━━━ Output Style ━━━ + .style("mixed") // prose | bullet-points | numbered-list | table | code | mixed | qa | dialogue + + // ━━━ Output Includes ━━━ + .withExamples() // Include examples in response + .withExplanation() // Include explanations + .withSources() // Cite sources + .withConfidence() // Include confidence level + + // ━━━ Reasoning ━━━ + .reasoning({ + style: "chain-of-thought", + showWork: true, + verifyAnswer: true, + considerAlternatives: true, + explainAssumptions: true + }) + .reasoningStyle("step-by-step") // step-by-step | chain-of-thought | tree-of-thought | direct | analytical | ... + .stepByStep() // Shortcut: step-by-step + showWork + .chainOfThought() // Shortcut: chain-of-thought + showWork + .treeOfThought() // Shortcut: tree-of-thought + showWork + .firstPrinciples() // Shortcut: first-principles + showWork + .devilsAdvocate() // Shortcut: devil-advocate + considerAlternatives + .showWork() // Show reasoning process + .verifyAnswer() // Verify before presenting + .considerAlternatives() // Consider alternatives + .explainAssumptions() // Explain assumptions + + // ━━━ Memory ━━━ + .memory({ + summary: "Previously discussed authentication patterns", + facts: ["User prefers JWT", "API uses REST"], + preferences: ["Concise answers", "Include code examples"], + history: [ + { role: "user", content: "How do I handle auth?" }, + { role: "assistant", content: "I recommend JWT with refresh tokens..." } + ] + }) + .remember(["User is building a SaaS app"]) // Add facts to memory + .preferences(["Show code examples"]) // User preferences + .history([ // Previous messages + { role: "user", content: "Previous question..." }, + { role: "assistant", content: "Previous answer..." } + ]) + .summarizeHistory("Discussed React patterns") // Summary of history + + // ━━━ Messages ━━━ + .system("You are a helpful assistant.") // System message + .user("Please review this code:", "developer") // User message (with optional name) + .assistant("I'll analyze the code now.") // Assistant message + .message("user", "Here's the code...") // Generic message + .messages([ // Multiple messages + { role: "user", content: "First question" }, + { role: "assistant", content: "First answer" } + ]) + .conversation([ // Conversation turns + { user: "What is X?", assistant: "X is..." }, + { user: "How does it work?" } + ]) + + // ━━━ Custom ━━━ + .addSystemPart("Additional system instructions") // Add to system prompt + .raw("Complete custom system prompt") // Replace with raw content + + .build(); + +// Access outputs +console.log(prompt.messages); // Array of ChatMessage objects +console.log(prompt.systemPrompt); // Built system prompt string +console.log(prompt.userPrompt); // Latest user message content +console.log(prompt.metadata); // Structured metadata + +// Export formats +const yaml = prompt.toYAML(); +const json = prompt.toJSON(); +const md = prompt.toMarkdown(); +const system = prompt.toSystemPrompt(); // Just the system prompt +const msgs = prompt.toMessages(); // Just the messages array +``` + +### Types Reference + +#### Message Types +```typescript +type MessageRole = 'system' | 'user' | 'assistant'; + +interface ChatMessage { + role: MessageRole; + content: string; + name?: string; +} +``` + +#### Persona Types +```typescript +type PersonaTone = + | 'professional' | 'casual' | 'formal' | 'friendly' | 'academic' + | 'technical' | 'creative' | 'empathetic' | 'authoritative' | 'playful' + | 'concise' | 'detailed' | 'socratic' | 'coaching' | 'analytical' + | 'encouraging' | 'neutral' | 'humorous' | 'serious'; + +type PersonaExpertise = + | 'general' | 'coding' | 'writing' | 'analysis' | 'research' + | 'teaching' | 'counseling' | 'creative' | 'legal' | 'medical' + | 'financial' | 'scientific' | 'engineering' | 'design' | 'marketing' + | 'business' | 'philosophy' | 'history' | 'languages' | 'mathematics'; +``` + +#### Reasoning Types +```typescript +type ReasoningStyle = + | 'step-by-step' | 'chain-of-thought' | 'tree-of-thought' + | 'direct' | 'analytical' | 'comparative' | 'deductive' | 'inductive' + | 'first-principles' | 'analogical' | 'devil-advocate'; +``` + +#### Output Types +```typescript +type ResponseFormatType = 'text' | 'json' | 'markdown' | 'code' | 'table'; + +type OutputLength = 'brief' | 'moderate' | 'detailed' | 'comprehensive' | 'exhaustive'; + +type OutputStyle = 'prose' | 'bullet-points' | 'numbered-list' | 'table' | 'code' | 'mixed' | 'qa' | 'dialogue'; + +interface JsonSchema { + name: string; + description?: string; + schema: Record; +} +``` + +### Methods Reference + +#### Message Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.system()` | `system(content: string)` | Set system message | +| `.user()` | `user(content: string, name?: string)` | Add user message | +| `.assistant()` | `assistant(content: string)` | Add assistant message | +| `.message()` | `message(role, content, name?)` | Add generic message | +| `.messages()` | `messages(messages: ChatMessage[])` | Add multiple messages | +| `.conversation()` | `conversation(turns[])` | Add conversation turns | + +#### Persona Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.persona()` | `persona(settings: ChatPersona \| string)` | Set full persona | +| `.role()` | `role(role: string)` | Set AI role | +| `.tone()` | `tone(tone: PersonaTone \| PersonaTone[])` | Set tone(s) | +| `.expertise()` | `expertise(areas: PersonaExpertise \| PersonaExpertise[])` | Set expertise | +| `.personality()` | `personality(traits: string[])` | Set personality traits | +| `.background()` | `background(background: string)` | Set background info | +| `.speakAs()` | `speakAs(name: string)` | Set persona name | +| `.responseLanguage()` | `responseLanguage(language: string)` | Set response language | + +#### Context Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.context()` | `context(settings: ChatContext \| string)` | Set full context | +| `.domain()` | `domain(domain: string)` | Set knowledge domain | +| `.audience()` | `audience(audience: string)` | Set target audience | +| `.purpose()` | `purpose(purpose: string)` | Set purpose | +| `.constraints()` | `constraints(constraints: string[])` | Add constraints | +| `.constraint()` | `constraint(constraint: string)` | Add single constraint | +| `.assumptions()` | `assumptions(assumptions: string[])` | Set assumptions | +| `.knowledge()` | `knowledge(facts: string[])` | Set known facts | + +#### Task Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.task()` | `task(instruction: string \| ChatTask)` | Set task | +| `.instruction()` | `instruction(instruction: string)` | Set main instruction | +| `.steps()` | `steps(steps: string[])` | Set task steps | +| `.deliverables()` | `deliverables(deliverables: string[])` | Set deliverables | +| `.criteria()` | `criteria(criteria: string[])` | Set success criteria | +| `.avoid()` | `avoid(antiPatterns: string[])` | Set anti-patterns | +| `.priority()` | `priority(priority)` | Set priority | + +#### Example Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.example()` | `example(input, output, explanation?)` | Add single example | +| `.examples()` | `examples(examples: ChatExample[])` | Add multiple examples | +| `.fewShot()` | `fewShot(examples[])` | Add few-shot examples | + +#### Output Format Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.output()` | `output(settings: ChatOutput)` | Set full output settings | +| `.outputFormat()` | `outputFormat(format: ResponseFormatType)` | Set format type | +| `.json()` | `json(schema?: JsonSchema)` | JSON output | +| `.jsonSchema()` | `jsonSchema(name, schema, description?)` | JSON with schema | +| `.markdown()` | `markdown()` | Markdown output | +| `.code()` | `code(language?: string)` | Code output | +| `.table()` | `table()` | Table output | + +#### Output Length Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.length()` | `length(length: OutputLength)` | Set length | +| `.brief()` | `brief()` | Brief output | +| `.moderate()` | `moderate()` | Moderate output | +| `.detailed()` | `detailed()` | Detailed output | +| `.comprehensive()` | `comprehensive()` | Comprehensive output | +| `.exhaustive()` | `exhaustive()` | Exhaustive output | + +#### Output Style Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.style()` | `style(style: OutputStyle)` | Set output style | +| `.withExamples()` | `withExamples()` | Include examples | +| `.withExplanation()` | `withExplanation()` | Include explanations | +| `.withSources()` | `withSources()` | Cite sources | +| `.withConfidence()` | `withConfidence()` | Include confidence | + +#### Reasoning Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.reasoning()` | `reasoning(settings: ChatReasoning)` | Set full reasoning | +| `.reasoningStyle()` | `reasoningStyle(style: ReasoningStyle)` | Set style | +| `.stepByStep()` | `stepByStep()` | Step-by-step reasoning | +| `.chainOfThought()` | `chainOfThought()` | Chain-of-thought | +| `.treeOfThought()` | `treeOfThought()` | Tree-of-thought | +| `.firstPrinciples()` | `firstPrinciples()` | First principles | +| `.devilsAdvocate()` | `devilsAdvocate()` | Devil's advocate | +| `.showWork()` | `showWork(show?)` | Show reasoning | +| `.verifyAnswer()` | `verifyAnswer(verify?)` | Verify answer | +| `.considerAlternatives()` | `considerAlternatives(consider?)` | Consider alternatives | +| `.explainAssumptions()` | `explainAssumptions(explain?)` | Explain assumptions | + +#### Memory Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.memory()` | `memory(memory: ChatMemory)` | Set full memory | +| `.remember()` | `remember(facts: string[])` | Add facts | +| `.preferences()` | `preferences(prefs: string[])` | Set preferences | +| `.history()` | `history(messages: ChatMessage[])` | Set history | +| `.summarizeHistory()` | `summarizeHistory(summary: string)` | Set summary | + +#### Custom & Output Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.addSystemPart()` | `addSystemPart(part: string)` | Add system part | +| `.raw()` | `raw(content: string)` | Set raw content | +| `.build()` | `build(): BuiltChatPrompt` | Build the prompt | +| `.toString()` | `toString(): string` | Get system prompt | +| `.toSystemPrompt()` | `toSystemPrompt(): string` | Get system prompt | +| `.toMessages()` | `toMessages(): ChatMessage[]` | Get messages | +| `.toJSON()` | `toJSON(): string` | Export as JSON | +| `.toYAML()` | `toYAML(): string` | Export as YAML | +| `.toMarkdown()` | `toMarkdown(): string` | Export as Markdown | + +### Output Structure + +```typescript +interface BuiltChatPrompt { + messages: ChatMessage[]; // Full message array including system + systemPrompt: string; // Built system prompt + userPrompt?: string; // Latest user message content + metadata: { + persona?: ChatPersona; + context?: ChatContext; + task?: ChatTask; + output?: ChatOutput; + reasoning?: ChatReasoning; + examples?: ChatExample[]; + }; +} +``` + +### Presets + +Pre-configured builders for common use cases: + +```typescript +import { chatPresets } from 'prompts.chat'; + +// Expert coder +const coder = chatPresets.coder("TypeScript") + .task("Review this function") + .user("function add(a, b) { return a + b }"); + +// Creative/Professional/Academic writer +const writer = chatPresets.writer("creative") + .task("Write a short story"); + +// Patient tutor with subject expertise +const tutor = chatPresets.tutor("mathematics") + .user("Explain derivatives"); + +// Data analyst with chain-of-thought +const analyst = chatPresets.analyst() + .user("Analyze these sales figures"); + +// Socratic philosopher (asks questions) +const socratic = chatPresets.socratic() + .user("What is justice?"); + +// Constructive critic +const critic = chatPresets.critic() + .user("Review my business plan"); + +// Creative brainstormer +const brainstormer = chatPresets.brainstormer() + .user("Ideas for a mobile app"); + +// JSON-only responder with schema +const jsonBot = chatPresets.jsonResponder("Response", { + type: "object", + properties: { + answer: { type: "string" }, + confidence: { type: "number" } + } +}); + +// Summarizer with length control +const summarizer = chatPresets.summarizer("brief") + .user("Summarize this article..."); + +// Translator to target language +const translator = chatPresets.translator("Japanese") + .user("Hello, how are you?"); +``` + +### Usage Examples + +#### Code Review Assistant +```typescript +const codeReview = chat() + .role("senior code reviewer") + .expertise("coding") + .tone(["professional", "constructive"]) + .context("TypeScript React project") + .task("Review code for bugs, performance, and best practices") + .steps([ + "Identify potential bugs", + "Check for performance issues", + "Suggest improvements" + ]) + .criteria(["Be specific", "Provide examples"]) + .avoid(["Harsh criticism", "Vague feedback"]) + .markdown() + .detailed() + .withExamples() + .user("Please review this component...") + .build(); +``` + +#### Research Assistant +```typescript +const research = chat() + .role("research assistant") + .expertise(["research", "analysis"]) + .tone("academic") + .context({ + domain: "machine learning", + audience: "PhD students", + purpose: "literature review" + }) + .task("Analyze and summarize research papers") + .chainOfThought() + .withSources() + .detailed() + .markdown() + .build(); +``` + +#### JSON API Response +```typescript +const apiHelper = chat() + .role("API response generator") + .tone("concise") + .jsonSchema("UserData", { + type: "object", + properties: { + users: { + type: "array", + items: { + type: "object", + properties: { + id: { type: "number" }, + name: { type: "string" }, + email: { type: "string" } + } + } + }, + total: { type: "number" } + } + }) + .avoid(["Include markdown", "Add explanations", "Use code fences"]) + .user("Generate 3 sample users") + .build(); +``` + +#### Multi-Turn Conversation +```typescript +const conversation = chat() + .role("helpful assistant") + .tone("friendly") + .memory({ + summary: "User is learning Python", + facts: ["User is a beginner", "Prefers practical examples"], + preferences: ["Step-by-step explanations"] + }) + .conversation([ + { user: "What is a variable?", assistant: "A variable is a container for storing data..." }, + { user: "How do I create one?" } + ]) + .stepByStep() + .withExamples() + .build(); +``` + +--- + +## 🎨 Image Builder + +Comprehensive builder for image generation prompts. Works with Midjourney, DALL-E, Stable Diffusion, Flux, and other image AI platforms. + +### Quick Start + +```typescript +import { image } from 'prompts.chat'; + +const prompt = image() + .subject("a lone samurai") + .environment("bamboo forest at dawn") + .camera({ angle: "low-angle", shot: "wide", lens: "35mm" }) + .lighting({ type: "rim", time: "golden-hour" }) + .medium("cinematic") + .build(); + +console.log(prompt.prompt); +``` + +### Full Example + +```typescript +import { image } from 'prompts.chat'; + +const prompt = image() + // ━━━ Subject ━━━ + .subject({ + main: "a cyberpunk samurai warrior", + expression: "determined and fierce", + pose: "dynamic battle stance", + action: "deflecting bullets with katana", + clothing: "neon-lit armor with glowing circuits", + accessories: ["holographic visor", "cyber-enhanced arm"], + age: "30s", + gender: "female" + }) + .subjectDetails(["intricate tattoos", "flowing hair"]) // Additional details + .expression("intense focus") // Override expression + .pose("mid-swing attack") // Override pose + .action("slicing through rain droplets") // Override action + .clothing("black nano-fiber suit") // Override clothing + .accessories(["glowing katana", "shoulder armor"]) // Override accessories + .subjectCount("single") // single | couple | group | crowd | number + + // ━━━ Environment ━━━ + .environment({ + setting: "rain-soaked Tokyo alley", + location: "Shibuya district", + atmosphere: "electric and mysterious", + props: ["neon signs", "steam vents", "holographic ads"], + season: "winter" + }) + .location("Neo-Tokyo, 2087") // Specific location + .props(["flying cars overhead", "robot vendors"]) // Scene props + .atmosphere("dense fog with neon glow") // Atmospheric description + .season("autumn") // spring | summer | autumn | winter + + // ━━━ Camera: Framing ━━━ + .shot("medium") // extreme-close-up | close-up | medium | wide | ... + .angle("low-angle") // eye-level | low-angle | high-angle | dutch-angle | ... + .lens("35mm") // wide-angle | 35mm | 50mm | 85mm | telephoto | ... + .focalLength("35mm") // Specific focal length + + // ━━━ Camera: Focus & Depth ━━━ + .focus("shallow") // shallow | deep | soft-focus | tilt-shift | bokeh-heavy | ... + .aperture("f/1.4") // Aperture setting + .bokeh("smooth") // smooth | creamy | swirly | soap-bubble | oval-anamorphic | ... + + // ━━━ Camera: Equipment ━━━ + .cameraBrand("sony") // sony | canon | nikon | leica | hasselblad | arri | ... + .cameraModel("sony-a7riv") // Specific camera model + .sensor("full-frame") // full-frame | aps-c | medium-format | ... + .lensBrand("zeiss") // zeiss | leica | sigma | canon | ... + .lensModel("zeiss-otus-55") // Specific lens model + + // ━━━ Camera: Film & Filters ━━━ + .filmStock("kodak-portra-400") // Kodak, Fujifilm, CineStill, Ilford stocks + .filmFormat("35mm") // 35mm | 120-medium-format | instant-film | ... + .filter("black-pro-mist") // nd | polarizer | black-pro-mist | diffusion | ... + + // ━━━ Camera: Exposure ━━━ + .iso(800) // ISO sensitivity + .shutterSpeed("1/250") // Shutter speed + .whiteBalance("tungsten") // daylight | cloudy | tungsten | fluorescent | ... + .colorProfile("S-Log3") // Color profile + + // ━━━ Lighting ━━━ + .lighting({ + type: ["rim", "practical"], + time: "night", + direction: "back", + intensity: "dramatic", + color: "neon pink and blue", + sources: ["neon signs", "car headlights"] + }) + .lightingType(["rim", "backlit"]) // natural | studio | rim | rembrandt | butterfly | ... + .timeOfDay("night") // dawn | golden-hour | midday | blue-hour | night | ... + .weather("rainy") // sunny | cloudy | foggy | rainy | stormy | snowy | ... + .lightDirection("back") // front | side | back | top | three-quarter + .lightIntensity("dramatic") // soft | medium | hard | dramatic + + // ━━━ Composition ━━━ + .composition({ + ruleOfThirds: true, + symmetry: "none", + foreground: "rain droplets", + midground: "samurai figure", + background: "towering neon buildings" + }) + .ruleOfThirds() // Enable rule of thirds + .goldenRatio() // Enable golden ratio + .symmetry("vertical") // none | horizontal | vertical | radial + .foreground("splashing water droplets") // Foreground element + .midground("central figure in action") // Midground element + .background("city skyline with flying vehicles") // Background element + + // ━━━ Style ━━━ + .style({ + medium: ["cinematic", "cyberpunk"], + artist: ["Syd Mead", "Simon Stålenhag"], + era: "futuristic", + influence: ["Blade Runner", "Ghost in the Shell"], + quality: ["highly detailed", "award-winning"] + }) + .medium("cinematic") // photorealistic | cinematic | anime | oil-painting | ... + .artist(["Syd Mead", "Masamune Shirow"]) // Reference artist(s) + .influence(["Akira", "The Matrix"]) // Style influences + + // ━━━ Color ━━━ + .color({ + palette: "neon", + primary: ["cyan", "magenta"], + accent: ["yellow", "white"], + grade: "teal and orange", + temperature: "cool", + saturation: "vibrant", + contrast: "high" + }) + .palette("neon") // warm | cool | vibrant | neon | monochrome | ... + .primaryColors(["electric blue", "hot pink"]) // Primary color scheme + .accentColors(["neon yellow", "white"]) // Accent colors + .colorGrade("cyberpunk teal and orange") // Color grading style + + // ━━━ Technical ━━━ + .technical({ + aspectRatio: "16:9", + resolution: "8K", + quality: "masterpiece", + detail: "extreme", + noise: "filmic", + sharpness: "crisp" + }) + .aspectRatio("16:9") // 1:1 | 4:3 | 3:2 | 16:9 | 21:9 | 9:16 | ... + .resolution("8K") // Resolution string + .quality("masterpiece") // draft | standard | high | ultra | masterpiece + + // ━━━ Mood & Misc ━━━ + .mood(["dramatic", "mysterious", "epic"]) // serene | dramatic | tense | epic | intimate | ... + .negative(["blurry", "low quality", "watermark"]) // Negative prompt items + .custom("volumetric lighting through rain") // Custom prompt text + .custom("lens flare from neon signs") + + .build(); + +// Access outputs +console.log(prompt.prompt); // Full formatted prompt with --no and --ar flags +console.log(prompt.structure); // Full structured data object + +// Export formats +const yaml = prompt.toYAML(); +const json = prompt.toJSON(); +const md = prompt.toMarkdown(); +``` + +### Types Reference + +#### Camera Types +```typescript +type ShotType = 'extreme-close-up' | 'close-up' | 'medium-close-up' | 'medium' + | 'medium-wide' | 'wide' | 'extreme-wide' | 'establishing' | 'full-body' | 'portrait' | 'headshot'; + +type CameraAngle = 'eye-level' | 'low-angle' | 'high-angle' | 'dutch-angle' | 'birds-eye' + | 'worms-eye' | 'over-the-shoulder' | 'point-of-view' | 'aerial' | 'drone' + | 'canted' | 'oblique' | 'hip-level' | 'knee-level' | 'ground-level'; + +type LensType = 'wide-angle' | 'ultra-wide' | 'standard' | 'telephoto' | 'macro' | 'fisheye' + | '14mm' | '24mm' | '35mm' | '50mm' | '85mm' | '100mm' | '135mm' | '200mm' | '400mm' + | 'tilt-shift' | 'anamorphic' | 'spherical' | 'prime' | 'zoom'; + +type FocusType = 'shallow' | 'deep' | 'soft-focus' | 'tilt-shift' | 'rack-focus' | 'split-diopter' + | 'zone-focus' | 'hyperfocal' | 'selective' | 'bokeh-heavy' | 'tack-sharp'; + +type BokehStyle = 'smooth' | 'creamy' | 'swirly' | 'busy' | 'soap-bubble' | 'cat-eye' | 'oval-anamorphic'; +``` + +#### Equipment Types +```typescript +type CameraBrand = 'sony' | 'canon' | 'nikon' | 'fujifilm' | 'leica' | 'hasselblad' | 'phase-one' + | 'panasonic' | 'olympus' | 'pentax' | 'red' | 'arri' | 'blackmagic' | 'panavision'; + +type CameraModel = 'sony-a7iv' | 'sony-a7riv' | 'sony-a1' | 'canon-r5' | 'canon-r6' + | 'nikon-z9' | 'nikon-z8' | 'leica-m11' | 'leica-q3' | 'hasselblad-x2d' + | 'fujifilm-x-t5' | 'fujifilm-gfx100s' | 'arri-alexa-35' | 'red-v-raptor' | ...; + +type SensorFormat = 'full-frame' | 'aps-c' | 'micro-four-thirds' | 'medium-format' | 'large-format' + | 'super-35' | 'vista-vision' | 'imax' | '65mm' | '35mm-film' | '16mm-film' | '8mm-film'; + +type LensBrand = 'zeiss' | 'leica' | 'canon' | 'nikon' | 'sony' | 'sigma' | 'tamron' | 'voigtlander' + | 'fujifilm' | 'samyang' | 'rokinon' | 'tokina' | 'cooke' | 'arri' | 'panavision'; + +type LensModel = 'zeiss-otus-55' | 'zeiss-batis-85' | 'leica-summilux-50' | 'leica-noctilux-50' + | 'canon-rf-50-1.2' | 'sony-gm-85-1.4' | 'sigma-art-35' | 'helios-44-2' | ...; +``` + +#### Film & Filter Types +```typescript +type FilmStock = + // Kodak Color + | 'kodak-portra-160' | 'kodak-portra-400' | 'kodak-portra-800' | 'kodak-ektar-100' | 'kodak-gold-200' + // Kodak B&W + | 'kodak-tri-x-400' | 'kodak-tmax-100' | 'kodak-tmax-400' + // Kodak Cinema + | 'kodak-vision3-50d' | 'kodak-vision3-200t' | 'kodak-vision3-500t' + // Fujifilm + | 'fujifilm-pro-400h' | 'fujifilm-velvia-50' | 'fujifilm-velvia-100' | 'fujifilm-provia-100f' + // Ilford + | 'ilford-hp5-plus' | 'ilford-delta-400' | 'ilford-fp4-plus' + // CineStill + | 'cinestill-50d' | 'cinestill-800t' + // Instant + | 'polaroid-sx-70' | 'polaroid-600' | 'instax-mini' | ...; + +type FilmFormat = '35mm' | '120-medium-format' | '4x5-large-format' | '8x10-large-format' + | '110-film' | 'instant-film' | 'super-8' | '16mm' | '65mm-imax'; + +type FilterType = 'uv' | 'polarizer' | 'nd' | 'nd-graduated' | 'black-pro-mist' | 'white-pro-mist' + | 'glimmer-glass' | 'classic-soft' | 'streak' | 'starburst' | 'diffusion' + | 'infrared' | 'color-gel' | 'warming' | 'cooling' | 'vintage-look'; +``` + +#### Lighting Types +```typescript +type LightingType = 'natural' | 'studio' | 'dramatic' | 'soft' | 'hard' | 'diffused' + | 'key' | 'fill' | 'rim' | 'backlit' | 'silhouette' | 'rembrandt' + | 'split' | 'butterfly' | 'loop' | 'broad' | 'short' | 'chiaroscuro' + | 'high-key' | 'low-key' | 'three-point' | 'practical' | 'motivated'; + +type TimeOfDay = 'dawn' | 'sunrise' | 'golden-hour' | 'morning' | 'midday' | 'afternoon' + | 'blue-hour' | 'sunset' | 'dusk' | 'twilight' | 'night' | 'midnight'; + +type WeatherLighting = 'sunny' | 'cloudy' | 'overcast' | 'foggy' | 'misty' + | 'rainy' | 'stormy' | 'snowy' | 'hazy'; +``` + +#### Style & Color Types +```typescript +type ArtStyle = 'photorealistic' | 'hyperrealistic' | 'cinematic' | 'documentary' + | 'editorial' | 'fashion' | 'portrait' | 'landscape' | 'street' | 'fine-art' + | 'conceptual' | 'surreal' | 'abstract' | 'minimalist' | 'maximalist' + | 'vintage' | 'retro' | 'noir' | 'gothic' | 'romantic' + | 'impressionist' | 'expressionist' | 'pop-art' | 'art-nouveau' | 'art-deco' + | 'cyberpunk' | 'steampunk' | 'fantasy' | 'sci-fi' | 'anime' | 'manga' + | 'comic-book' | 'illustration' | 'digital-art' | 'oil-painting' | 'watercolor' + | 'sketch' | 'pencil-drawing' | 'charcoal' | 'pastel' | '3d-render'; + +type ColorPalette = 'warm' | 'cool' | 'neutral' | 'vibrant' | 'muted' | 'pastel' | 'neon' + | 'monochrome' | 'sepia' | 'desaturated' | 'high-contrast' | 'low-contrast' + | 'earthy' | 'oceanic' | 'forest' | 'sunset' | 'midnight' | 'golden'; + +type Mood = 'serene' | 'peaceful' | 'melancholic' | 'dramatic' | 'tense' | 'mysterious' + | 'romantic' | 'nostalgic' | 'hopeful' | 'joyful' | 'energetic' | 'chaotic' + | 'ethereal' | 'dark' | 'light' | 'whimsical' | 'eerie' | 'epic' | 'intimate'; + +type AspectRatio = '1:1' | '4:3' | '3:2' | '16:9' | '21:9' | '9:16' | '2:3' | '4:5' | '5:4'; +``` + +### Methods Reference + +#### Subject Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.subject()` | `subject(main: string \| ImageSubject)` | Set main subject | +| `.subjectDetails()` | `subjectDetails(details: string[])` | Add subject details | +| `.expression()` | `expression(expression: string)` | Set facial expression | +| `.pose()` | `pose(pose: string)` | Set body pose | +| `.action()` | `action(action: string)` | Set action/activity | +| `.clothing()` | `clothing(clothing: string)` | Set clothing | +| `.accessories()` | `accessories(accessories: string[])` | Set accessories | +| `.subjectCount()` | `subjectCount(count)` | Set subject count | + +#### Environment Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.environment()` | `environment(setting: string \| ImageEnvironment)` | Set environment | +| `.location()` | `location(location: string)` | Set specific location | +| `.props()` | `props(props: string[])` | Set scene props | +| `.atmosphere()` | `atmosphere(atmosphere: string)` | Set atmosphere | +| `.season()` | `season(season)` | Set season | + +#### Camera Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.camera()` | `camera(settings: ImageCamera)` | Set all camera settings | +| `.shot()` | `shot(shot: ShotType)` | Set shot type | +| `.angle()` | `angle(angle: CameraAngle)` | Set camera angle | +| `.lens()` | `lens(lens: LensType)` | Set lens type | +| `.focus()` | `focus(focus: FocusType)` | Set focus type | +| `.aperture()` | `aperture(aperture: string)` | Set aperture | +| `.bokeh()` | `bokeh(style: BokehStyle)` | Set bokeh style | +| `.cameraBrand()` | `cameraBrand(brand: CameraBrand)` | Set camera brand | +| `.cameraModel()` | `cameraModel(model: CameraModel)` | Set camera model | +| `.sensor()` | `sensor(sensor: SensorFormat)` | Set sensor format | +| `.lensBrand()` | `lensBrand(brand: LensBrand)` | Set lens brand | +| `.lensModel()` | `lensModel(model: LensModel)` | Set lens model | +| `.focalLength()` | `focalLength(length: string)` | Set focal length | +| `.filmStock()` | `filmStock(stock: FilmStock)` | Set film stock | +| `.filmFormat()` | `filmFormat(format: FilmFormat)` | Set film format | +| `.filter()` | `filter(filter: FilterType)` | Set lens filter | +| `.iso()` | `iso(iso: number)` | Set ISO | +| `.shutterSpeed()` | `shutterSpeed(speed: string)` | Set shutter speed | +| `.whiteBalance()` | `whiteBalance(wb)` | Set white balance | +| `.colorProfile()` | `colorProfile(profile: string)` | Set color profile | + +#### Lighting Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.lighting()` | `lighting(settings: ImageLighting)` | Set all lighting | +| `.lightingType()` | `lightingType(type)` | Set lighting type(s) | +| `.timeOfDay()` | `timeOfDay(time: TimeOfDay)` | Set time of day | +| `.weather()` | `weather(weather: WeatherLighting)` | Set weather | +| `.lightDirection()` | `lightDirection(direction)` | Set light direction | +| `.lightIntensity()` | `lightIntensity(intensity)` | Set light intensity | + +#### Composition Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.composition()` | `composition(settings: ImageComposition)` | Set all composition | +| `.ruleOfThirds()` | `ruleOfThirds()` | Enable rule of thirds | +| `.goldenRatio()` | `goldenRatio()` | Enable golden ratio | +| `.symmetry()` | `symmetry(type)` | Set symmetry type | +| `.foreground()` | `foreground(fg: string)` | Set foreground | +| `.midground()` | `midground(mg: string)` | Set midground | +| `.background()` | `background(bg: string)` | Set background | + +#### Style Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.style()` | `style(settings: ImageStyle)` | Set all style settings | +| `.medium()` | `medium(medium: ArtStyle)` | Set art style/medium | +| `.artist()` | `artist(artist: string \| string[])` | Set reference artist(s) | +| `.influence()` | `influence(influences: string[])` | Set style influences | + +#### Color Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.color()` | `color(settings: ImageColor)` | Set all color settings | +| `.palette()` | `palette(palette: ColorPalette)` | Set color palette | +| `.primaryColors()` | `primaryColors(colors: string[])` | Set primary colors | +| `.accentColors()` | `accentColors(colors: string[])` | Set accent colors | +| `.colorGrade()` | `colorGrade(grade: string)` | Set color grade | + +#### Technical & Output Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.technical()` | `technical(settings: ImageTechnical)` | Set all technical | +| `.aspectRatio()` | `aspectRatio(ratio: AspectRatio)` | Set aspect ratio | +| `.resolution()` | `resolution(resolution: string)` | Set resolution | +| `.quality()` | `quality(quality)` | Set quality level | +| `.mood()` | `mood(mood: Mood \| Mood[])` | Set mood(s) | +| `.negative()` | `negative(items: string[])` | Add negative prompts | +| `.custom()` | `custom(text: string)` | Add custom text | +| `.build()` | `build(): BuiltImagePrompt` | Build the prompt | +| `.toString()` | `toString(): string` | Get prompt string | +| `.toJSON()` | `toJSON(): string` | Export as JSON | +| `.toYAML()` | `toYAML(): string` | Export as YAML | +| `.toMarkdown()` | `toMarkdown(): string` | Export as Markdown | + +### Output Structure + +```typescript +interface BuiltImagePrompt { + prompt: string; // Full formatted prompt with --no and --ar flags + structure: { + subject?: ImageSubject; + camera?: ImageCamera; + lighting?: ImageLighting; + composition?: ImageComposition; + style?: ImageStyle; + color?: ImageColor; + environment?: ImageEnvironment; + technical?: ImageTechnical; + mood?: Mood | Mood[]; + negative?: string[]; + }; +} +``` + +### Usage Examples + +#### Portrait Photography +```typescript +const portrait = image() + .subject({ + main: "elderly fisherman", + expression: "weathered but kind smile", + age: "70s", + clothing: "worn yellow raincoat" + }) + .environment("misty harbor at dawn") + .shot("close-up") + .angle("eye-level") + .lens("85mm") + .focus("shallow") + .aperture("f/1.8") + .lightingType("natural") + .timeOfDay("golden-hour") + .medium("portrait") + .filmStock("kodak-portra-400") + .mood("nostalgic") + .quality("masterpiece") + .build(); +``` + +#### Fantasy Illustration +```typescript +const fantasy = image() + .subject({ + main: "elven queen", + expression: "regal and mysterious", + pose: "seated on crystal throne", + clothing: "flowing silver gown with starlight patterns", + accessories: ["crown of moonstone", "ancient scepter"] + }) + .environment({ + setting: "ethereal forest palace", + atmosphere: "magical mist and floating lights", + props: ["glowing flowers", "ancient trees"] + }) + .medium(["fantasy", "illustration"]) + .artist(["Alan Lee", "Brian Froud"]) + .palette("cool") + .primaryColors(["silver", "deep blue", "violet"]) + .lightingType("soft") + .mood(["ethereal", "mysterious"]) + .aspectRatio("2:3") + .quality("ultra") + .negative(["modern elements", "technology"]) + .build(); +``` + +#### Product Photography +```typescript +const product = image() + .subject("luxury mechanical watch") + .subjectDetails(["intricate movement visible", "sapphire crystal"]) + .environment("minimalist studio setup") + .props(["black velvet", "subtle reflections"]) + .shot("extreme-close-up") + .lens("macro") + .focus("tack-sharp") + .aperture("f/8") + .lightingType(["studio", "rim"]) + .lightDirection("side") + .lightIntensity("soft") + .medium("commercial") + .palette("monochrome") + .colorGrade("high contrast, deep blacks") + .aspectRatio("1:1") + .resolution("8K") + .quality("masterpiece") + .build(); +``` + +#### Cinematic Landscape +```typescript +const landscape = image() + .environment({ + setting: "volcanic Iceland highlands", + location: "Landmannalaugar", + terrain: "colorful rhyolite mountains", + atmosphere: "dramatic storm clouds breaking" + }) + .season("summer") + .shot("extreme-wide") + .angle("aerial") + .lens("wide-angle") + .cameraBrand("hasselblad") + .cameraModel("hasselblad-x2d") + .sensor("medium-format") + .timeOfDay("golden-hour") + .weather("stormy") + .lightingType("dramatic") + .medium("landscape") + .palette(["earthy", "vibrant"]) + .primaryColors(["rust red", "moss green", "volcanic black"]) + .mood(["epic", "dramatic"]) + .aspectRatio("21:9") + .quality("ultra") + .build(); +``` + +--- + +## 🎬 Video Builder + +Comprehensive builder for video generation prompts. Works with Sora, Runway, Pika, Kling, and other video AI platforms. + +### Quick Start + +```typescript +import { video } from 'prompts.chat'; + +const prompt = video() + .scene("A samurai walks through a bamboo forest") + .camera({ movement: "tracking", angle: "low-angle" }) + .lighting({ time: "golden-hour", type: "natural" }) + .duration(5) + .build(); + +console.log(prompt.prompt); +``` + +### Full Example + +```typescript +import { video } from 'prompts.chat'; + +const prompt = video() + // ━━━ Scene ━━━ + .scene({ + description: "A lone astronaut walks across the Martian surface", + setting: "Olympus Mons base camp", + atmosphere: "dusty red landscape, thin atmosphere" + }) + .setting("Mars, near the base camp") // Additional location context + .timeOfDay("golden-hour") // dawn | sunrise | golden-hour | midday | blue-hour | night | ... + .weather("foggy") // sunny | cloudy | overcast | foggy | rainy | stormy | snowy | ... + + // ━━━ Subject ━━━ + .subject({ + main: "astronaut", + appearance: "NASA spacesuit with reflective gold visor", + clothing: "white EVA suit with mission patches", + age: "30s", + gender: "female" + }) + .appearance("determined expression") // Additional appearance details + .clothing("dusty, worn spacesuit") // Override/add clothing + + // ━━━ Camera: Framing ━━━ + .shot("wide") // extreme-close-up | close-up | medium | wide | establishing | ... + .angle("low-angle") // eye-level | low-angle | high-angle | dutch-angle | birds-eye | ... + .lens("anamorphic") // wide-angle | 35mm | 50mm | 85mm | telephoto | anamorphic | ... + .focalLength("40mm") // Specific focal length + .anamorphic("2x") // Enable anamorphic with ratio + .aperture("f/2.8") // Aperture setting + + // ━━━ Camera: Movement ━━━ + .movement("tracking") // static | pan | tilt | dolly | tracking | crane | steadicam | ... + .cameraSpeed("slow") // slow | medium | fast + .movementDirection("forward") // left | right | forward | backward | up | down | arc-left | arc-right + .platform("steadicam") // handheld | steadicam | tripod | drone | crane | gimbal | dolly | ... + .rig("slider") // tripod | gimbal | steadicam | crane | dolly | slider | ... + .gimbal("dji-ronin-4d") // Specific gimbal model + + // ━━━ Camera: Equipment ━━━ + .cameraBrand("arri") // arri | red | sony | canon | blackmagic | panavision | ... + .cameraModel("arri-alexa-65") // Specific camera model + .sensor("65mm") // full-frame | super-35 | 65mm | imax | ... + .lensBrand("cooke") // zeiss | cooke | arri | panavision | ... + .lensModel("cooke-anamorphic") // Specific lens model + + // ━━━ Camera: Technical ━━━ + .frameRate(24) // 24 | 25 | 30 | 48 | 60 | 120 | 240 + .slowMotion() // Enable slow motion + .shutterAngle(180) // Shutter angle in degrees + .filter("black-pro-mist") // nd | polarizer | black-pro-mist | diffusion | ... + .filmStock("kodak-vision3-500t") // Kodak, Fujifilm, CineStill film stocks + .filmGrain("subtle") // none | subtle | moderate | heavy + .halation() // Enable film halation effect + + // ━━━ Lighting ━━━ + .lighting({ + type: "natural", + time: "golden-hour", + direction: "back", + intensity: "soft" + }) + .lightingType(["natural", "rim"]) // natural | studio | dramatic | rim | rembrandt | ... + + // ━━━ Actions ━━━ + .action("walks slowly toward camera") // Add action beat + .action("stops and looks at horizon", { timing: "middle" }) + .action("raises hand to shield eyes", { timing: "end" }) + .actions(["turns around", "begins walking back"]) // Add multiple actions + + // ━━━ Motion ━━━ + .motion({ + subject: "astronaut", + type: "walk", + direction: "forward", + speed: "slow" + }) + .motionBeats(["step", "pause", "step", "turn"]) // Detailed motion beats + + // ━━━ Style ━━━ + .style({ + format: "cinematic", + era: "2020s", + look: "cinematic" + }) + .look("cinematic") // photorealistic | cinematic | documentary | sci-fi | noir | ... + .era("futuristic") // 1970s | 1980s | modern | futuristic | ... + .format("widescreen epic") // Format description + .styleFilmStock("Kodak Vision3 500T") // Style reference film stock + .reference(["Interstellar", "The Martian", "Gravity"]) // Reference films/directors + + // ━━━ Color ━━━ + .color({ + palette: "warm", + temperature: "warm", + grade: "orange and teal" + }) + .palette("warm") // warm | cool | neutral | vibrant | neon | monochrome | ... + .colorAnchors(["mars red", "suit white", "visor gold"]) // Key colors + .colorGrade("orange and teal") // Color grading style + + // ━━━ Audio ━━━ + .audio({ + ambient: "wind howling", + music: "epic orchestral, building tension", + dialogue: "breathing sounds in helmet" + }) + .dialogue("Houston, I've arrived at the site") // Character dialogue + .ambient("martian wind, distant rumbling") // Ambient sounds + .diegetic(["footsteps on gravel", "suit servos"]) // In-world sounds + .soundEffects(["radio static", "helmet HUD beeps"]) // Sound effects + .music("Hans Zimmer style, building brass") // Music description + + // ━━━ Technical ━━━ + .technical({ + duration: 10, + resolution: "4K", + fps: 24, + aspectRatio: "21:9" + }) + .duration(10) // Duration in seconds + .resolution("4K") // 480p | 720p | 1080p | 4K + .fps(24) // 24 | 30 | 60 + .aspectRatio("21:9") // 16:9 | 9:16 | 1:1 | 4:3 | 21:9 + + // ━━━ Mood & Pacing ━━━ + .mood(["epic", "mysterious"]) // serene | dramatic | tense | epic | intimate | ... + .pacing("slow") // slow | medium | fast | variable | building | contemplative + .transition("fade") // cut | fade | dissolve | wipe | morph | match-cut | ... + .transitions(["fade", "dissolve"]) // Multiple transitions + + // ━━━ Shot List ━━━ + .addShot({ + name: "Opening wide", + camera: { shot: "extreme-wide", movement: "static" }, + action: "Establish the Martian landscape", + purpose: "Set the scene" + }) + .shotList([ + { camera: { shot: "medium", movement: "tracking" }, action: "Follow astronaut" }, + { camera: { shot: "close-up", angle: "low-angle" }, action: "Hero shot" } + ]) + + // ━━━ Custom ━━━ + .custom("Lens flare as sun peeks over horizon") + .custom("Dust particles visible in backlight") + + .build(); + +// Access outputs +console.log(prompt.prompt); // Full formatted prompt +console.log(prompt.structure); // Full structured data object + +// Export formats +const yaml = prompt.toYAML(); +const json = prompt.toJSON(); +const md = prompt.toMarkdown(); +``` + +### Types Reference + +#### Camera Types +```typescript +type ShotType = 'extreme-close-up' | 'close-up' | 'medium-close-up' | 'medium' + | 'medium-wide' | 'wide' | 'extreme-wide' | 'establishing' | 'full-body' | 'portrait' | 'headshot'; + +type CameraAngle = 'eye-level' | 'low-angle' | 'high-angle' | 'dutch-angle' | 'birds-eye' + | 'worms-eye' | 'over-the-shoulder' | 'point-of-view' | 'aerial' | 'drone' + | 'canted' | 'oblique' | 'hip-level' | 'knee-level' | 'ground-level'; + +type CameraMovement = 'static' | 'pan' | 'tilt' | 'dolly' | 'truck' | 'pedestal' | 'zoom' + | 'handheld' | 'steadicam' | 'crane' | 'drone' | 'tracking' | 'arc' | 'whip-pan' + | 'roll' | 'boom' | 'jib' | 'cable-cam' | 'motion-control' | 'snorricam' + | 'dutch-roll' | 'vertigo-effect' | 'crash-zoom' | 'slow-push' | 'slow-pull'; + +type LensType = 'wide-angle' | 'ultra-wide' | 'standard' | 'telephoto' | 'macro' | 'fisheye' + | '14mm' | '24mm' | '35mm' | '50mm' | '85mm' | '100mm' | '135mm' | '200mm' + | 'tilt-shift' | 'anamorphic' | 'spherical' | 'prime' | 'zoom'; +``` + +#### Equipment Types +```typescript +type CameraBrand = 'sony' | 'canon' | 'nikon' | 'fujifilm' | 'leica' | 'hasselblad' + | 'red' | 'arri' | 'blackmagic' | 'panavision' | 'panasonic'; + +type CameraModel = 'arri-alexa-35' | 'arri-alexa-mini-lf' | 'arri-alexa-65' + | 'red-v-raptor' | 'red-komodo' | 'sony-venice' | 'sony-fx6' + | 'canon-c70' | 'blackmagic-ursa-mini-pro' | 'panavision-dxl2' | ...; + +type CameraRig = 'tripod' | 'monopod' | 'gimbal' | 'steadicam' | 'easyrig' | 'shoulder-rig' + | 'slider' | 'dolly' | 'jib' | 'crane' | 'technocrane' | 'russian-arm' + | 'cable-cam' | 'drone' | 'fpv-drone' | 'motion-control' | 'handheld'; + +type GimbalModel = 'dji-ronin-4d' | 'dji-ronin-rs3-pro' | 'dji-ronin-rs4' + | 'moza-air-2' | 'zhiyun-crane-3s' | 'freefly-movi-pro' | 'tilta-gravity-g2x'; +``` + +#### Lighting Types +```typescript +type LightingType = 'natural' | 'studio' | 'dramatic' | 'soft' | 'hard' | 'diffused' + | 'key' | 'fill' | 'rim' | 'backlit' | 'silhouette' | 'rembrandt' + | 'split' | 'butterfly' | 'loop' | 'broad' | 'short' | 'chiaroscuro' + | 'high-key' | 'low-key' | 'three-point' | 'practical' | 'motivated'; + +type TimeOfDay = 'dawn' | 'sunrise' | 'golden-hour' | 'morning' | 'midday' | 'afternoon' + | 'blue-hour' | 'sunset' | 'dusk' | 'twilight' | 'night' | 'midnight'; + +type WeatherLighting = 'sunny' | 'cloudy' | 'overcast' | 'foggy' | 'misty' + | 'rainy' | 'stormy' | 'snowy' | 'hazy'; +``` + +#### Style & Color Types +```typescript +type ArtStyle = 'photorealistic' | 'hyperrealistic' | 'cinematic' | 'documentary' + | 'editorial' | 'fashion' | 'portrait' | 'landscape' | 'street' | 'fine-art' + | 'surreal' | 'abstract' | 'minimalist' | 'vintage' | 'retro' | 'noir' + | 'cyberpunk' | 'steampunk' | 'fantasy' | 'sci-fi' | 'anime' | '3d-render'; + +type ColorPalette = 'warm' | 'cool' | 'neutral' | 'vibrant' | 'muted' | 'pastel' | 'neon' + | 'monochrome' | 'sepia' | 'desaturated' | 'high-contrast' | 'low-contrast' + | 'earthy' | 'oceanic' | 'forest' | 'sunset' | 'midnight' | 'golden'; + +type Mood = 'serene' | 'peaceful' | 'melancholic' | 'dramatic' | 'tense' | 'mysterious' + | 'romantic' | 'nostalgic' | 'hopeful' | 'joyful' | 'energetic' | 'chaotic' + | 'ethereal' | 'dark' | 'light' | 'whimsical' | 'eerie' | 'epic' | 'intimate'; + +type FilmStock = 'kodak-portra-400' | 'kodak-vision3-500t' | 'kodak-vision3-50d' + | 'fujifilm-eterna-500t' | 'cinestill-800t' | 'ilford-hp5-plus' | ...; +``` + +#### Video-Specific Types +```typescript +type VideoPacing = 'slow' | 'medium' | 'fast' | 'variable' | 'building' | 'frenetic' | 'contemplative'; + +type VideoTransition = 'cut' | 'fade' | 'dissolve' | 'wipe' | 'morph' | 'match-cut' + | 'jump-cut' | 'cross-dissolve' | 'iris' | 'push' | 'slide'; + +type FilterType = 'uv' | 'polarizer' | 'nd' | 'nd-graduated' | 'black-pro-mist' + | 'white-pro-mist' | 'glimmer-glass' | 'classic-soft' | 'diffusion' | 'infrared'; +``` + +### Methods Reference + +#### Scene Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.scene()` | `scene(description: string \| VideoScene)` | Set scene description | +| `.setting()` | `setting(setting: string)` | Set location/setting | +| `.timeOfDay()` | `timeOfDay(time: TimeOfDay)` | Set time of day | +| `.weather()` | `weather(weather: WeatherLighting)` | Set weather conditions | + +#### Subject Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.subject()` | `subject(main: string \| VideoSubject)` | Set main subject | +| `.appearance()` | `appearance(appearance: string)` | Set appearance details | +| `.clothing()` | `clothing(clothing: string)` | Set clothing/costume | + +#### Camera Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.camera()` | `camera(settings: VideoCamera)` | Set all camera settings | +| `.shot()` | `shot(shot: ShotType)` | Set shot type | +| `.angle()` | `angle(angle: CameraAngle)` | Set camera angle | +| `.movement()` | `movement(movement: CameraMovement)` | Set camera movement | +| `.lens()` | `lens(lens: LensType)` | Set lens type | +| `.platform()` | `platform(platform)` | Set camera platform | +| `.cameraSpeed()` | `cameraSpeed(speed)` | Set movement speed | +| `.movementDirection()` | `movementDirection(direction)` | Set movement direction | +| `.rig()` | `rig(rig: CameraRig)` | Set camera rig | +| `.gimbal()` | `gimbal(gimbal: GimbalModel)` | Set gimbal model | +| `.cameraBrand()` | `cameraBrand(brand: CameraBrand)` | Set camera brand | +| `.cameraModel()` | `cameraModel(model: CameraModel)` | Set camera model | +| `.sensor()` | `sensor(sensor: SensorFormat)` | Set sensor format | +| `.lensBrand()` | `lensBrand(brand: LensBrand)` | Set lens brand | +| `.lensModel()` | `lensModel(model: LensModel)` | Set lens model | +| `.focalLength()` | `focalLength(length: string)` | Set focal length | +| `.anamorphic()` | `anamorphic(ratio?)` | Enable anamorphic | +| `.aperture()` | `aperture(aperture: string)` | Set aperture | +| `.frameRate()` | `frameRate(fps)` | Set frame rate | +| `.slowMotion()` | `slowMotion(enabled?)` | Enable slow motion | +| `.shutterAngle()` | `shutterAngle(angle: number)` | Set shutter angle | +| `.filter()` | `filter(filter: FilterType)` | Set lens filter | +| `.filmStock()` | `filmStock(stock: FilmStock)` | Set film stock | +| `.filmGrain()` | `filmGrain(grain)` | Set film grain level | +| `.halation()` | `halation(enabled?)` | Enable halation effect | + +#### Lighting Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.lighting()` | `lighting(settings: VideoLighting)` | Set all lighting | +| `.lightingType()` | `lightingType(type)` | Set lighting type(s) | + +#### Action & Motion Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.action()` | `action(action, options?)` | Add single action | +| `.actions()` | `actions(actions: string[])` | Add multiple actions | +| `.motion()` | `motion(settings: VideoMotion)` | Set motion settings | +| `.motionBeats()` | `motionBeats(beats: string[])` | Set motion beats | + +#### Style Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.style()` | `style(settings: VideoStyle)` | Set all style settings | +| `.look()` | `look(look: ArtStyle)` | Set visual look | +| `.era()` | `era(era: string)` | Set era/time period | +| `.format()` | `format(format: string)` | Set format description | +| `.styleFilmStock()` | `styleFilmStock(stock: string)` | Set style film stock | +| `.reference()` | `reference(refs: string[])` | Set reference films | + +#### Color Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.color()` | `color(settings: VideoColor)` | Set all color settings | +| `.palette()` | `palette(palette: ColorPalette)` | Set color palette | +| `.colorAnchors()` | `colorAnchors(anchors: string[])` | Set key colors | +| `.colorGrade()` | `colorGrade(grade: string)` | Set color grade | + +#### Audio Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.audio()` | `audio(settings: VideoAudio)` | Set all audio | +| `.dialogue()` | `dialogue(dialogue: string)` | Set dialogue | +| `.ambient()` | `ambient(ambient: string)` | Set ambient sound | +| `.diegetic()` | `diegetic(sounds: string[])` | Set diegetic sounds | +| `.soundEffects()` | `soundEffects(effects: string[])` | Set sound effects | +| `.music()` | `music(music: string)` | Set music description | + +#### Technical Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.technical()` | `technical(settings: VideoTechnical)` | Set all technical | +| `.duration()` | `duration(seconds: number)` | Set duration | +| `.resolution()` | `resolution(res)` | Set resolution | +| `.fps()` | `fps(fps)` | Set frame rate | +| `.aspectRatio()` | `aspectRatio(ratio)` | Set aspect ratio | + +#### Mood, Pacing & Output Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.mood()` | `mood(mood: Mood \| Mood[])` | Set mood(s) | +| `.pacing()` | `pacing(pacing: VideoPacing)` | Set pacing | +| `.transition()` | `transition(transition)` | Add transition | +| `.transitions()` | `transitions(transitions[])` | Add transitions | +| `.addShot()` | `addShot(shot: VideoShot)` | Add to shot list | +| `.shotList()` | `shotList(shots: VideoShot[])` | Set shot list | +| `.custom()` | `custom(text: string)` | Add custom text | +| `.build()` | `build(): BuiltVideoPrompt` | Build the prompt | +| `.toString()` | `toString(): string` | Get prompt string | +| `.toJSON()` | `toJSON(): string` | Export as JSON | +| `.toYAML()` | `toYAML(): string` | Export as YAML | +| `.toMarkdown()` | `toMarkdown(): string` | Export as Markdown | + +### Output Structure + +```typescript +interface BuiltVideoPrompt { + prompt: string; // Full formatted prompt + structure: { + scene?: VideoScene; + subject?: VideoSubject; + camera?: VideoCamera; + lighting?: VideoLighting; + actions?: VideoAction[]; + motion?: VideoMotion; + style?: VideoStyle; + color?: VideoColor; + audio?: VideoAudio; + technical?: VideoTechnical; + shots?: VideoShot[]; + mood?: Mood | Mood[]; + pacing?: VideoPacing; + transitions?: VideoTransition[]; + }; +} +``` + +### Usage Examples + +#### Cinematic Product Shot +```typescript +const product = video() + .scene("Luxury watch rotating on black velvet") + .shot("extreme-close-up") + .movement("slow-push") + .lens("100mm") + .lightingType(["rim", "key"]) + .look("commercial") + .palette("monochrome") + .pacing("slow") + .duration(5) + .resolution("4K") + .build(); +``` + +#### Documentary Interview +```typescript +const interview = video() + .scene("Expert speaking in modern office") + .subject({ main: "scientist", appearance: "professional attire" }) + .shot("medium") + .angle("eye-level") + .movement("static") + .platform("tripod") + .lightingType("three-point") + .look("documentary") + .dialogue("Explaining climate change impacts") + .ambient("quiet office hum") + .pacing("medium") + .aspectRatio("16:9") + .build(); +``` + +#### Action Sequence +```typescript +const action = video() + .scene("Car chase through city streets") + .subject("sports car") + .shot("tracking") + .movement("tracking") + .cameraSpeed("fast") + .platform("fpv-drone") + .lens("wide-angle") + .lightingType("natural") + .timeOfDay("night") + .look("cinematic") + .colorGrade("high contrast, neon") + .pacing("frenetic") + .actions([ + "Car drifts around corner", + "Sparks fly from undercarriage", + "Near miss with oncoming traffic" + ]) + .soundEffects(["engine roar", "tire screech", "horns"]) + .music("intense electronic, pulsing bass") + .fps(60) + .slowMotion() + .duration(8) + .build(); +``` + +#### Nature Documentary +```typescript +const nature = video() + .scene("Lion stalking prey across African savanna") + .subject({ main: "lion", appearance: "adult male, golden mane" }) + .setting("Serengeti National Park") + .shot("wide") + .lens("telephoto") + .cameraBrand("red") + .cameraModel("red-v-raptor") + .movement("static") + .platform("tripod") + .timeOfDay("golden-hour") + .weather("sunny") + .look("documentary") + .palette("warm") + .mood("tense") + .pacing("slow") + .ambient("wind through grass, distant wildlife") + .music("subtle, building tension") + .resolution("4K") + .fps(60) + .aspectRatio("21:9") + .build(); +``` + +--- + +## 🎵 Audio Builder + +Comprehensive builder for music and audio generation prompts. Works with Suno, Udio, and other music AI platforms. + +### Quick Start + +```typescript +import { audio } from 'prompts.chat'; + +const prompt = audio() + .genre("synthwave") + .mood("nostalgic", "dreamy") + .bpm(110) + .instruments(["synthesizer", "drums", "bass"]) + .build(); + +console.log(prompt.stylePrompt); // "synthwave, nostalgic, dreamy, 110 BPM, synthesizer, drums, bass" +``` + +### Full Example + +```typescript +import { audio } from 'prompts.chat'; + +const prompt = audio() + // ━━━ Genre ━━━ + .genre("electronic") // Primary genre + .subgenre("synthwave") // Subgenre + .fusion(["rock", "pop"]) // Genre fusion + + // ━━━ Mood & Energy ━━━ + .mood("nostalgic", "triumphant", "hopeful") // Primary + secondary moods + .energy("high") // low | medium | high | building | fluctuating + .emotion("euphoria") // Custom emotion descriptor + + // ━━━ Tempo ━━━ + .bpm(120) // Beats per minute + .tempoMarking("allegro") // Classical tempo marking + .tempoFeel("driving") // steady | swung | shuffled | syncopated | rubato | driving + + // ━━━ Vocals ━━━ + .vocalStyle("melodic") // male | female | duet | choir | rap | falsetto | ... + .language("english") // english | spanish | japanese | instrumental | ... + .lyricsTheme("retro-futuristic dreams and neon nights") + .lyrics("[Verse 1]\nRiding through the neon glow...") + .delivery("powerful and emotional") + .instrumental() // Shortcut for instrumental (no vocals) + + // ━━━ Instrumentation ━━━ + .instruments(["synthesizer", "drums", "bass"]) // Quick instrument list + .leadInstrument("synthesizer") // Lead melody instrument + .rhythmSection(["drums", "bass"]) // Rhythm section + .bassInstrument("bass") // Bass instrument + .percussion(["drums", "808"]) // Percussion + .pads(["synthesizer"]) // Pad sounds + .featuredInstrument("electric-guitar") // Featured solo instrument + + // ━━━ Structure ━━━ + .section("intro", 8, "atmospheric synth pad build") + .section("verse", 16, "driving rhythm with arpeggios") + .section("pre-chorus", 8, "tension build") + .section("chorus", 16, "anthemic, full instrumentation") + .section("verse", 16, "variation with added elements") + .section("chorus", 16, "bigger, more layers") + .section("bridge", 8, "breakdown, stripped back") + .section("drop", 16, "climactic peak") + .section("outro", 8, "fade out with echoing synths") + .duration(210) // Total duration in seconds (3:30) + .form("ABABCB") // Song form notation + + // ━━━ Production ━━━ + .productionStyle("polished") // lo-fi | hi-fi | vintage | modern | polished | raw | ... + .era("1980s") // 1950s-2020s | retro | vintage | modern | futuristic + .reference(["Kavinsky", "Carpenter Brut", "Perturbator"]) + .texture("lush and warm") + .effects(["reverb", "delay", "sidechain compression", "chorus"]) + + // ━━━ Technical ━━━ + .key("Am") // C | Am | G | Em | D | Bm | F# | Ebm | ... + .timeSignature("4/4") // 4/4 | 3/4 | 6/8 | 5/4 | 7/8 | 12/8 + .formatType("song") // song | instrumental | jingle | loop | soundtrack + + // ━━━ Tags & Custom ━━━ + .tag("80s") // Add single tag + .tags(["retro", "neon", "cinematic"]) // Add multiple tags + .custom("arpeggiated bassline throughout") // Custom text + + .build(); + +// Access outputs +console.log(prompt.prompt); // Full formatted prompt +console.log(prompt.stylePrompt); // Style-only prompt (for Suno/Udio style field) +console.log(prompt.lyricsPrompt); // Lyrics prompt (if lyrics provided) +console.log(prompt.structure); // Full structured data object + +// Export formats +const yaml = prompt.toYAML(); +const json = prompt.toJSON(); +const md = prompt.toMarkdown(); +``` + +### Types Reference + +#### MusicGenre +```typescript +type MusicGenre = + | 'pop' | 'rock' | 'jazz' | 'classical' | 'electronic' | 'hip-hop' | 'r&b' + | 'country' | 'folk' | 'blues' | 'metal' | 'punk' | 'indie' | 'alternative' + | 'ambient' | 'lo-fi' | 'synthwave' | 'orchestral' | 'cinematic' | 'world' + | 'latin' | 'reggae' | 'soul' | 'funk' | 'disco' | 'house' | 'techno' | 'edm' + | 'trap' | 'drill' | 'k-pop' | 'j-pop' | 'bossa-nova' | 'gospel' | 'grunge' + | 'shoegaze' | 'post-rock' | 'prog-rock' | 'psychedelic' | 'chillwave' + | 'vaporwave' | 'drum-and-bass' | 'dubstep' | 'trance' | 'hardcore'; +``` + +#### Instrument +```typescript +type Instrument = + | 'piano' | 'guitar' | 'acoustic-guitar' | 'electric-guitar' | 'bass' | 'drums' + | 'violin' | 'cello' | 'viola' | 'flute' | 'saxophone' | 'trumpet' | 'trombone' + | 'synthesizer' | 'organ' | 'harp' | 'percussion' | 'strings' | 'brass' | 'woodwinds' + | 'choir' | 'vocals' | 'beatbox' | 'turntables' | 'harmonica' | 'banjo' | 'ukulele' + | 'mandolin' | 'accordion' | 'marimba' | 'vibraphone' | 'xylophone' | 'timpani' + | 'congas' | 'bongos' | 'djembe' | 'tabla' | 'sitar' | 'erhu' | 'koto' + | '808' | '909' | 'moog' | 'rhodes' | 'wurlitzer' | 'mellotron' | 'theremin'; +``` + +#### VocalStyle +```typescript +type VocalStyle = + | 'male' | 'female' | 'duet' | 'choir' | 'a-cappella' | 'spoken-word' | 'rap' + | 'falsetto' | 'belting' | 'whisper' | 'growl' | 'melodic' | 'harmonized' + | 'auto-tuned' | 'operatic' | 'soul' | 'breathy' | 'nasal' | 'raspy' | 'clear'; +``` + +#### Other Types +```typescript +type VocalLanguage = 'english' | 'spanish' | 'french' | 'german' | 'italian' + | 'portuguese' | 'japanese' | 'korean' | 'chinese' | 'arabic' | 'hindi' + | 'russian' | 'turkish' | 'instrumental'; + +type TempoMarking = 'largo' | 'adagio' | 'andante' | 'moderato' | 'allegro' | 'vivace' | 'presto'; + +type TimeSignature = '4/4' | '3/4' | '6/8' | '2/4' | '5/4' | '7/8' | '12/8'; + +type MusicalKey = 'C' | 'C#' | 'Db' | 'D' | 'D#' | 'Eb' | 'E' | 'F' | 'F#' | 'Gb' + | 'G' | 'G#' | 'Ab' | 'A' | 'A#' | 'Bb' | 'B' + | 'Cm' | 'C#m' | 'Dm' | 'D#m' | 'Ebm' | 'Em' | 'Fm' | 'F#m' + | 'Gm' | 'G#m' | 'Am' | 'A#m' | 'Bbm' | 'Bm'; + +type SongSection = 'intro' | 'verse' | 'pre-chorus' | 'chorus' | 'bridge' + | 'breakdown' | 'drop' | 'build-up' | 'outro' | 'solo' | 'interlude' | 'hook'; + +type ProductionStyle = 'lo-fi' | 'hi-fi' | 'vintage' | 'modern' | 'polished' | 'raw' + | 'organic' | 'synthetic' | 'acoustic' | 'electric' | 'hybrid' | 'minimalist' + | 'maximalist' | 'layered' | 'sparse' | 'dense' | 'atmospheric' | 'punchy' | 'warm' | 'bright'; + +type Era = '1950s' | '1960s' | '1970s' | '1980s' | '1990s' | '2000s' | '2010s' | '2020s' + | 'retro' | 'vintage' | 'classic' | 'modern' | 'futuristic'; +``` + +### Methods Reference + +#### Genre Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.genre()` | `genre(primary: MusicGenre \| AudioGenre)` | Set primary genre | +| `.subgenre()` | `subgenre(subgenre: string)` | Set subgenre | +| `.fusion()` | `fusion(genres: MusicGenre[])` | Blend multiple genres | + +#### Mood Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.mood()` | `mood(primary, ...secondary)` | Set moods (variadic) | +| `.energy()` | `energy(level)` | Set energy level | +| `.emotion()` | `emotion(emotion: string)` | Set emotion descriptor | + +#### Tempo Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.tempo()` | `tempo(bpm \| AudioTempo)` | Set tempo (BPM or object) | +| `.bpm()` | `bpm(bpm: number)` | Set beats per minute | +| `.tempoMarking()` | `tempoMarking(marking: TempoMarking)` | Classical tempo term | +| `.tempoFeel()` | `tempoFeel(feel)` | Set rhythmic feel | + +#### Vocal Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.vocals()` | `vocals(settings: AudioVocals)` | Set all vocal settings | +| `.vocalStyle()` | `vocalStyle(style: VocalStyle \| VocalStyle[])` | Set vocal style(s) | +| `.language()` | `language(lang: VocalLanguage)` | Set language | +| `.lyrics()` | `lyrics(lyrics: string)` | Set lyrics text | +| `.lyricsTheme()` | `lyricsTheme(theme: string)` | Set lyrics theme | +| `.delivery()` | `delivery(delivery: string)` | Set vocal delivery | +| `.instrumental()` | `instrumental()` | Make instrumental (no vocals) | + +#### Instrumentation Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.instruments()` | `instruments(instruments: Instrument[])` | Set instrument list | +| `.instrumentation()` | `instrumentation(settings: AudioInstrumentation)` | Full settings object | +| `.leadInstrument()` | `leadInstrument(instrument)` | Set lead instrument | +| `.rhythmSection()` | `rhythmSection(instruments: Instrument[])` | Set rhythm section | +| `.bassInstrument()` | `bassInstrument(instrument: Instrument)` | Set bass | +| `.percussion()` | `percussion(instruments)` | Set percussion | +| `.pads()` | `pads(instruments)` | Set pad sounds | +| `.featuredInstrument()` | `featuredInstrument(instrument: Instrument)` | Featured instrument | + +#### Structure Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.structure()` | `structure(settings \| { [section]: bars })` | Set structure | +| `.section()` | `section(type, bars?, description?)` | Add song section | +| `.form()` | `form(form: string)` | Set song form (e.g., "ABABCB") | +| `.duration()` | `duration(seconds: number)` | Set total duration | + +#### Production Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.production()` | `production(settings: AudioProduction)` | Full production settings | +| `.productionStyle()` | `productionStyle(style)` | Set production style | +| `.era()` | `era(era: Era)` | Set era/decade | +| `.reference()` | `reference(artists: string[])` | Reference artists | +| `.texture()` | `texture(texture: string)` | Set sound texture | +| `.effects()` | `effects(effects: string[])` | Add audio effects | + +#### Technical Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.technical()` | `technical(settings: AudioTechnical)` | Full technical settings | +| `.key()` | `key(key: MusicalKey)` | Set musical key | +| `.timeSignature()` | `timeSignature(sig: TimeSignature)` | Set time signature | +| `.formatType()` | `formatType(format)` | Set format type | + +#### Tags & Output Methods +| Method | Signature | Description | +|--------|-----------|-------------| +| `.tag()` | `tag(tag: string)` | Add single tag | +| `.tags()` | `tags(tags: string[])` | Add multiple tags | +| `.custom()` | `custom(text: string)` | Add custom text | +| `.build()` | `build(): BuiltAudioPrompt` | Build the prompt | +| `.toString()` | `toString(): string` | Get prompt string | +| `.toStyleString()` | `toStyleString(): string` | Get style prompt only | +| `.toJSON()` | `toJSON(): string` | Export as JSON | +| `.toYAML()` | `toYAML(): string` | Export as YAML | +| `.toMarkdown()` | `toMarkdown(): string` | Export as Markdown | + +### Output Structure + +```typescript +interface BuiltAudioPrompt { + prompt: string; // Full formatted prompt + stylePrompt: string; // Style-only prompt (for AI style field) + lyricsPrompt?: string; // Lyrics prompt (if lyrics/theme provided) + structure: { + genre?: AudioGenre; + mood?: AudioMood; + tempo?: AudioTempo; + vocals?: AudioVocals; + instrumentation?: AudioInstrumentation; + structure?: AudioStructure; + production?: AudioProduction; + technical?: AudioTechnical; + tags?: string[]; + }; +} +``` + +### Usage Examples + +#### Lo-Fi Hip-Hop Beat +```typescript +const lofi = audio() + .genre("lo-fi") + .subgenre("chill hop") + .mood("relaxed", "nostalgic") + .bpm(85) + .tempoFeel("swung") + .instrumental() + .instruments(["rhodes", "drums", "bass", "vinyl-crackle"]) + .productionStyle("lo-fi") + .texture("warm and dusty") + .effects(["vinyl crackle", "tape saturation", "low-pass filter"]) + .build(); +``` + +#### Epic Orchestral Soundtrack +```typescript +const epic = audio() + .genre("orchestral") + .subgenre("epic cinematic") + .mood("epic", "triumphant", "powerful") + .energy("building") + .tempoMarking("moderato") + .bpm(100) + .instrumental() + .instruments(["strings", "brass", "timpani", "choir", "percussion"]) + .section("intro", 16, "quiet strings, building tension") + .section("build-up", 32, "brass enters, crescendo") + .section("drop", 32, "full orchestra, powerful theme") + .section("outro", 16, "triumphant resolution") + .productionStyle(["polished", "layered"]) + .era("modern") + .key("Dm") + .build(); +``` + +#### Pop Song with Lyrics +```typescript +const popSong = audio() + .genre("pop") + .mood("uplifting", "energetic") + .bpm(120) + .vocalStyle("female") + .language("english") + .lyricsTheme("summer love and freedom") + .lyrics(`[Verse 1] +Dancing in the summer rain +Nothing left to lose, nothing to explain + +[Chorus] +We're alive, we're on fire +Take me higher, higher`) + .instruments(["synth", "drums", "bass", "electric-guitar"]) + .section("intro", 8) + .section("verse", 16) + .section("chorus", 16) + .section("verse", 16) + .section("chorus", 16) + .section("bridge", 8) + .section("chorus", 16) + .section("outro", 8) + .productionStyle("polished") + .key("G") + .build(); +``` + +--- + +## ✅ Quality + +Local prompt quality validation (no API required). + +```typescript +import { quality } from 'prompts.chat'; + +// Check prompt quality +const result = quality.check("Act as a senior developer..."); + +console.log(result.valid); // true +console.log(result.score); // 0.85 (0-1) +console.log(result.issues); // Array of issues +console.log(result.stats); // Detailed statistics + +// Statistics include: +// - characterCount, wordCount, sentenceCount +// - variableCount +// - hasRole, hasTask, hasConstraints, hasExamples + +// Validate (throws if invalid) +quality.validate(promptText); + +// Check validity +const isValid = quality.isValid(promptText); + +// Get improvement suggestions +const suggestions = quality.getSuggestions(promptText); +// → ["Add a role definition", "Consider adding examples"] +``` + +### Quality Checks + +| Check | Type | Description | +|-------|------|-------------| +| `EMPTY` | error | Prompt is empty | +| `TOO_SHORT` | error | Below minimum length | +| `GIBBERISH` | error | Random/keyboard patterns | +| `FEW_WORDS` | warning | Very few words | +| `UNBALANCED_BRACKETS` | warning | Mismatched brackets | +| `LONG_LINES` | suggestion | Lines over 500 chars | +| `NO_CLEAR_INSTRUCTION` | suggestion | Missing role or task | + +--- + +## 📄 Parser + +Parse prompt files in YAML, JSON, Markdown, and plain text formats. + +```typescript +import { parser } from 'prompts.chat'; + +// Auto-detect format +const prompt = parser.parse(content); + +// Parse YAML +const yamlPrompt = parser.parse(` +name: Code Review +model: gpt-4 +modelParameters: + temperature: 0.7 +messages: + - role: system + content: You are a code reviewer. +`, 'yaml'); + +// Parse JSON +const jsonPrompt = parser.parse(`{ + "name": "Assistant", + "messages": [{"role": "system", "content": "You are helpful."}] +}`, 'json'); + +// Parse Markdown with frontmatter +const mdPrompt = parser.parse(` +--- +name: Creative Writer +model: gpt-4 +--- +You are a creative writing assistant. +`, 'markdown'); + +// Parse plain text (becomes system message) +const textPrompt = parser.parse("You are a helpful assistant.", 'text'); + +// Serialize +const yaml = parser.toYaml(prompt); +const json = parser.toJson(prompt, true); // pretty print + +// Get system message +const systemPrompt = parser.getSystemPrompt(prompt); + +// Interpolate variables +const filled = parser.interpolate(prompt, { name: "John" }); +``` + +### ParsedPrompt Structure + +```typescript +interface ParsedPrompt { + name?: string; + description?: string; + model?: string; + modelParameters?: { + temperature?: number; + maxTokens?: number; + topP?: number; + frequencyPenalty?: number; + presencePenalty?: number; + }; + messages: PromptMessage[]; + variables?: Record; + metadata?: Record; +} +``` + +--- + +## Tree-Shakeable Imports + +Import only what you need for smaller bundles: + +```typescript +// Full namespace imports +import { variables, similarity, quality, parser } from 'prompts.chat'; + +// Direct builder imports +import { builder, chat, image, video, audio } from 'prompts.chat'; +import { templates, chatPresets } from 'prompts.chat'; + +// Direct module imports (smallest bundle) +import { detect, normalize, compile } from 'prompts.chat/variables'; +import { calculate, isDuplicate } from 'prompts.chat/similarity'; +import { check, validate, getSuggestions } from 'prompts.chat/quality'; +import { parse, toYaml, toJson } from 'prompts.chat/parser'; +import { builder, templates } from 'prompts.chat/builder'; +``` + +--- + +## TypeScript Support + +Full TypeScript support with comprehensive type exports: + +```typescript +import type { + // Variables + DetectedVariable, + VariablePattern, + + // Builder + BuiltPrompt, + PromptVariable, + + // Chat + BuiltChatPrompt, + ChatMessage, + ChatPersona, + PersonaTone, + ReasoningStyle, + + // Image + BuiltImagePrompt, + ImageSubject, + ImageCamera, + CameraAngle, + ShotType, + LensType, + + // Video + BuiltVideoPrompt, + VideoScene, + VideoCamera, + + // Audio + BuiltAudioPrompt, + MusicGenre, + Instrument, + + // Quality + QualityResult, + QualityIssue, + + // Parser + ParsedPrompt, + PromptMessage, +} from 'prompts.chat'; +``` + +--- + +## Requirements + +- **Node.js** 18+ +- **TypeScript** 5+ (optional, for type checking) + +--- + +## Testing + +```bash +npm test # Run all tests +npm run test:watch # Watch mode +npm run test:coverage # With coverage +``` + +--- + +## License + +MIT © [Fatih Kadir Akın](https://github.com/f) diff --git a/packages/prompts.chat/package-lock.json b/packages/prompts.chat/package-lock.json new file mode 100644 index 00000000..553e686c --- /dev/null +++ b/packages/prompts.chat/package-lock.json @@ -0,0 +1,1920 @@ +{ + "name": "prompts.chat", + "version": "0.0.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "prompts.chat", + "version": "0.0.2", + "license": "MIT", + "devDependencies": { + "@types/node": "^20", + "tsup": "^8.0.0", + "typescript": "^5", + "vitest": "^4.0.16" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz", + "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz", + "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", + "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz", + "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz", + "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz", + "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz", + "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz", + "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz", + "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz", + "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz", + "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz", + "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz", + "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz", + "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz", + "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz", + "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz", + "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz", + "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz", + "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz", + "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz", + "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz", + "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.27.tgz", + "integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.16.tgz", + "integrity": "sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.16", + "@vitest/utils": "4.0.16", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.16.tgz", + "integrity": "sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.16", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.16.tgz", + "integrity": "sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.16.tgz", + "integrity": "sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.16", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.16.tgz", + "integrity": "sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.16", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.16.tgz", + "integrity": "sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.16.tgz", + "integrity": "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.16", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", + "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.54.0", + "@rollup/rollup-android-arm64": "4.54.0", + "@rollup/rollup-darwin-arm64": "4.54.0", + "@rollup/rollup-darwin-x64": "4.54.0", + "@rollup/rollup-freebsd-arm64": "4.54.0", + "@rollup/rollup-freebsd-x64": "4.54.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", + "@rollup/rollup-linux-arm-musleabihf": "4.54.0", + "@rollup/rollup-linux-arm64-gnu": "4.54.0", + "@rollup/rollup-linux-arm64-musl": "4.54.0", + "@rollup/rollup-linux-loong64-gnu": "4.54.0", + "@rollup/rollup-linux-ppc64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-musl": "4.54.0", + "@rollup/rollup-linux-s390x-gnu": "4.54.0", + "@rollup/rollup-linux-x64-gnu": "4.54.0", + "@rollup/rollup-linux-x64-musl": "4.54.0", + "@rollup/rollup-openharmony-arm64": "4.54.0", + "@rollup/rollup-win32-arm64-msvc": "4.54.0", + "@rollup/rollup-win32-ia32-msvc": "4.54.0", + "@rollup/rollup-win32-x64-gnu": "4.54.0", + "@rollup/rollup-win32-x64-msvc": "4.54.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz", + "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.16.tgz", + "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.16", + "@vitest/mocker": "4.0.16", + "@vitest/pretty-format": "4.0.16", + "@vitest/runner": "4.0.16", + "@vitest/snapshot": "4.0.16", + "@vitest/spy": "4.0.16", + "@vitest/utils": "4.0.16", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.16", + "@vitest/browser-preview": "4.0.16", + "@vitest/browser-webdriverio": "4.0.16", + "@vitest/ui": "4.0.16", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/packages/prompts.chat/package.json b/packages/prompts.chat/package.json new file mode 100644 index 00000000..d0d18e95 --- /dev/null +++ b/packages/prompts.chat/package.json @@ -0,0 +1,82 @@ +{ + "name": "prompts.chat", + "version": "0.0.3", + "description": "Developer toolkit for AI prompts - build, validate, parse, and connect to prompts.chat", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + }, + "./variables": { + "types": "./dist/variables/index.d.ts", + "import": "./dist/variables/index.mjs", + "require": "./dist/variables/index.js" + }, + "./similarity": { + "types": "./dist/similarity/index.d.ts", + "import": "./dist/similarity/index.mjs", + "require": "./dist/similarity/index.js" + }, + "./quality": { + "types": "./dist/quality/index.d.ts", + "import": "./dist/quality/index.mjs", + "require": "./dist/quality/index.js" + }, + "./builder": { + "types": "./dist/builder/index.d.ts", + "import": "./dist/builder/index.mjs", + "require": "./dist/builder/index.js" + }, + "./parser": { + "types": "./dist/parser/index.d.ts", + "import": "./dist/parser/index.mjs", + "require": "./dist/parser/index.js" + } + }, + "files": [ + "bin", + "dist", + "README.md" + ], + "keywords": [ + "prompts", + "chatgpt", + "ai", + "llm", + "openai", + "anthropic", + "claude", + "prompt-engineering", + "prompt-templates", + "prompt-builder", + "cli" + ], + "repository": { + "type": "git", + "url": "https://github.com/f/awesome-chatgpt-prompts" + }, + "homepage": "https://prompts.chat", + "author": "Fatih Kadir Akın ", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" + }, + "devDependencies": { + "@types/node": "^20", + "tsup": "^8.0.0", + "typescript": "^5", + "vitest": "^4.0.16" + } +} diff --git a/packages/prompts.chat/src/__tests__/builder.test.ts b/packages/prompts.chat/src/__tests__/builder.test.ts new file mode 100644 index 00000000..a7c50125 --- /dev/null +++ b/packages/prompts.chat/src/__tests__/builder.test.ts @@ -0,0 +1,354 @@ +import { describe, it, expect } from 'vitest'; +import { + builder, + fromPrompt, + PromptBuilder, + templates, + chat, + ChatPromptBuilder, + chatPresets, + image, + ImagePromptBuilder, + video, + VideoPromptBuilder, + audio, + AudioPromptBuilder, +} from '../builder'; + +describe('builder module', () => { + describe('PromptBuilder', () => { + it('should create a builder with factory function', () => { + const b = builder(); + expect(b).toBeInstanceOf(PromptBuilder); + }); + + it('should build a prompt with role', () => { + const result = builder().role('helpful assistant').build(); + expect(result.content).toContain('You are helpful assistant'); + expect(result.metadata.role).toBe('helpful assistant'); + }); + + it('should build a prompt with context', () => { + const result = builder().context('some background').build(); + expect(result.content).toContain('Context'); + expect(result.content).toContain('some background'); + }); + + it('should build a prompt with task', () => { + const result = builder().task('do something').build(); + expect(result.content).toContain('Task'); + expect(result.content).toContain('do something'); + }); + + it('should build a prompt with constraints', () => { + const result = builder().constraints(['be concise', 'be helpful']).build(); + expect(result.content).toContain('Constraints'); + expect(result.content).toContain('be concise'); + expect(result.content).toContain('be helpful'); + }); + + it('should build a prompt with output format', () => { + const result = builder().output('JSON format').build(); + expect(result.content).toContain('Output Format'); + expect(result.content).toContain('JSON format'); + }); + + it('should build a prompt with examples', () => { + const result = builder().example('input', 'output').build(); + expect(result.content).toContain('Examples'); + expect(result.content).toContain('input'); + expect(result.content).toContain('output'); + }); + + it('should build a prompt with variables', () => { + const result = builder() + .variable('name', { required: true, description: 'The name' }) + .build(); + expect(result.variables).toHaveLength(1); + expect(result.variables[0].name).toBe('name'); + }); + + it('should chain multiple methods', () => { + const result = builder() + .role('assistant') + .context('helping') + .task('answer') + .constraint('be nice') + .build(); + + expect(result.metadata.role).toBe('assistant'); + expect(result.metadata.context).toBe('helping'); + expect(result.metadata.task).toBe('answer'); + }); + + it('toString should return content', () => { + const content = builder().role('assistant').toString(); + expect(content).toContain('You are assistant'); + }); + + it('fromPrompt should create raw prompt', () => { + const result = fromPrompt('raw content').build(); + expect(result.content).toBe('raw content'); + }); + }); + + describe('templates', () => { + it('should create code review template', () => { + const template = templates.codeReview({ language: 'TypeScript' }); + const result = template.build(); + expect(result.content).toContain('code reviewer'); + expect(result.content).toContain('TypeScript'); + }); + + it('should create translation template', () => { + const template = templates.translation('English', 'Spanish'); + const result = template.build(); + expect(result.content).toContain('translator'); + expect(result.content).toContain('English'); + expect(result.content).toContain('Spanish'); + }); + + it('should create summarize template', () => { + const template = templates.summarize({ maxLength: 100 }); + const result = template.build(); + expect(result.content).toContain('summarizer'); + expect(result.content).toContain('100'); + }); + + it('should create qa template', () => { + const template = templates.qa(); + const result = template.build(); + expect(result.content).toContain('helpful'); + }); + }); + + describe('ChatPromptBuilder', () => { + it('should create a chat builder with factory function', () => { + const c = chat(); + expect(c).toBeInstanceOf(ChatPromptBuilder); + }); + + it('should build with role', () => { + const result = chat().role('helpful assistant').build(); + expect(result.systemPrompt).toContain('You are helpful assistant'); + }); + + it('should build with tone', () => { + const result = chat().tone('professional').build(); + expect(result.systemPrompt).toContain('professional'); + }); + + it('should build with expertise', () => { + const result = chat().expertise('coding').build(); + expect(result.systemPrompt).toContain('coding'); + }); + + it('should build with context', () => { + const result = chat().context('building an app').build(); + expect(result.systemPrompt).toContain('building an app'); + }); + + it('should build with task', () => { + const result = chat().task('review code').build(); + expect(result.systemPrompt).toContain('review code'); + }); + + it('should build with constraints', () => { + const result = chat().constraints(['be concise']).build(); + expect(result.systemPrompt).toContain('be concise'); + }); + + it('should build with avoid', () => { + const result = chat().avoid(['being verbose']).build(); + expect(result.systemPrompt).toContain('being verbose'); + }); + + it('should add user message', () => { + const result = chat().user('Hello').build(); + expect(result.messages.some(m => m.role === 'user' && m.content === 'Hello')).toBe(true); + }); + + it('should add assistant message', () => { + const result = chat().assistant('Hi there').build(); + expect(result.messages.some(m => m.role === 'assistant' && m.content === 'Hi there')).toBe(true); + }); + + it('should build with examples', () => { + const result = chat().example('input', 'output').build(); + expect(result.systemPrompt).toContain('input'); + expect(result.systemPrompt).toContain('output'); + }); + + it('should build with reasoning style', () => { + const result = chat().stepByStep().build(); + expect(result.systemPrompt).toContain('step by step'); + }); + + it('should build with output format', () => { + const result = chat().json().build(); + expect(result.systemPrompt).toContain('JSON'); + }); + + it('should build with length', () => { + const result = chat().detailed().build(); + expect(result.systemPrompt).toContain('detailed'); + }); + + it('should chain multiple methods', () => { + const result = chat() + .role('developer') + .tone('technical') + .task('explain code') + .stepByStep() + .detailed() + .user('What is this?') + .build(); + + expect(result.systemPrompt).toContain('developer'); + expect(result.messages.length).toBeGreaterThan(1); + }); + + it('toSystemPrompt should return system prompt string', () => { + const systemPrompt = chat().role('assistant').toSystemPrompt(); + expect(systemPrompt).toContain('You are assistant'); + }); + + it('toMessages should return messages array', () => { + const messages = chat().user('Hello').toMessages(); + expect(Array.isArray(messages)).toBe(true); + }); + + it('toJSON should return JSON string', () => { + const json = chat().role('assistant').toJSON(); + expect(() => JSON.parse(json)).not.toThrow(); + }); + }); + + describe('chatPresets', () => { + it('should create coder preset', () => { + const result = chatPresets.coder('TypeScript').build(); + expect(result.systemPrompt).toContain('developer'); + }); + + it('should create writer preset', () => { + const result = chatPresets.writer('creative').build(); + expect(result.systemPrompt).toContain('writer'); + }); + + it('should create tutor preset', () => { + const result = chatPresets.tutor('math').build(); + expect(result.systemPrompt).toContain('tutor'); + }); + + it('should create analyst preset', () => { + const result = chatPresets.analyst().build(); + expect(result.systemPrompt).toContain('analyst'); + }); + + it('should create socratic preset', () => { + const result = chatPresets.socratic().build(); + expect(result.systemPrompt).toContain('Socratic'); + }); + }); + + describe('ImagePromptBuilder', () => { + it('should create an image builder with factory function', () => { + const i = image(); + expect(i).toBeInstanceOf(ImagePromptBuilder); + }); + + it('should build with subject', () => { + const result = image().subject('a cat').build(); + expect(result.prompt).toContain('cat'); + }); + + it('should build with environment', () => { + const result = image().environment('forest').build(); + expect(result.prompt).toContain('forest'); + }); + + it('should build with camera settings', () => { + const result = image() + .shot('close-up') + .angle('low-angle') + .lens('85mm') + .build(); + expect(result.prompt).toContain('close-up'); + expect(result.prompt).toContain('low-angle'); + expect(result.prompt).toContain('85mm'); + }); + + it('should build with lighting', () => { + const result = image() + .lightingType('rim') + .timeOfDay('golden-hour') + .build(); + expect(result.prompt).toContain('rim'); + expect(result.prompt).toContain('golden-hour'); + }); + + it('should build with style', () => { + const result = image() + .medium('cinematic') + .artist('Roger Deakins') + .build(); + expect(result.prompt).toContain('cinematic'); + expect(result.prompt).toContain('Roger Deakins'); + }); + + it('should output to JSON', () => { + const json = image().subject('test').toJSON(); + expect(() => JSON.parse(json)).not.toThrow(); + }); + }); + + describe('VideoPromptBuilder', () => { + it('should create a video builder with factory function', () => { + const v = video(); + expect(v).toBeInstanceOf(VideoPromptBuilder); + }); + + it('should build with scene', () => { + const result = video().scene('A car drives').build(); + expect(result.prompt).toContain('car drives'); + }); + + it('should build with camera movement', () => { + const result = video().movement('tracking').build(); + expect(result.prompt).toContain('tracking'); + }); + + it('should build with duration', () => { + const result = video().duration(5).build(); + expect(result.structure.technical?.duration).toBe(5); + }); + }); + + describe('AudioPromptBuilder', () => { + it('should create an audio builder with factory function', () => { + const a = audio(); + expect(a).toBeInstanceOf(AudioPromptBuilder); + }); + + it('should build with genre', () => { + const result = audio().genre('jazz').build(); + expect(result.stylePrompt).toContain('jazz'); + }); + + it('should build with mood', () => { + const result = audio().mood('melancholic').build(); + expect(result.stylePrompt).toContain('melancholic'); + }); + + it('should build with tempo', () => { + const result = audio().bpm(120).build(); + expect(result.stylePrompt).toContain('120'); + }); + + it('should build with instruments', () => { + const result = audio().instruments(['piano', 'drums']).build(); + expect(result.stylePrompt).toContain('piano'); + expect(result.stylePrompt).toContain('drums'); + }); + }); +}); diff --git a/packages/prompts.chat/src/__tests__/parser.test.ts b/packages/prompts.chat/src/__tests__/parser.test.ts new file mode 100644 index 00000000..b5494845 --- /dev/null +++ b/packages/prompts.chat/src/__tests__/parser.test.ts @@ -0,0 +1,180 @@ +import { describe, it, expect } from 'vitest'; +import { + parse, + toYaml, + toJson, + getSystemPrompt, + interpolate, +} from '../parser'; + +describe('parser module', () => { + describe('parse', () => { + it('should parse plain text as system message', () => { + const result = parse('You are a helpful assistant.'); + expect(result.messages).toHaveLength(1); + expect(result.messages[0].role).toBe('system'); + expect(result.messages[0].content).toBe('You are a helpful assistant.'); + }); + + it('should parse JSON format', () => { + const json = JSON.stringify({ + name: 'Test Prompt', + messages: [{ role: 'system', content: 'Hello' }], + }); + const result = parse(json); + expect(result.name).toBe('Test Prompt'); + expect(result.messages[0].content).toBe('Hello'); + }); + + it('should auto-detect JSON format', () => { + const result = parse('{"messages": [{"role": "system", "content": "Hello"}]}'); + expect(result.messages[0].content).toBe('Hello'); + }); + + it('should parse YAML format', () => { + const yaml = `name: Test +messages: + - role: system + content: Hello`; + const result = parse(yaml, 'yaml'); + expect(result.name).toBe('Test'); + expect(result.messages).toHaveLength(1); + }); + + it('should parse Markdown with frontmatter', () => { + const md = `--- +name: Test Prompt +--- +You are a helpful assistant.`; + const result = parse(md, 'markdown'); + expect(result.name).toBe('Test Prompt'); + expect(result.messages[0].content).toBe('You are a helpful assistant.'); + }); + + it('should handle content field as system message', () => { + const result = parse('{"content": "Hello World"}', 'json'); + expect(result.messages[0].content).toBe('Hello World'); + }); + + it('should handle prompt field as system message', () => { + const result = parse('{"prompt": "Hello World"}', 'json'); + expect(result.messages[0].content).toBe('Hello World'); + }); + + it('should parse model parameters', () => { + const json = JSON.stringify({ + model: 'gpt-4', + modelParameters: { temperature: 0.7 }, + messages: [{ role: 'system', content: 'Test' }], + }); + const result = parse(json); + expect(result.model).toBe('gpt-4'); + expect(result.modelParameters?.temperature).toBe(0.7); + }); + }); + + describe('toYaml', () => { + it('should serialize prompt to YAML', () => { + const prompt = { + name: 'Test', + messages: [{ role: 'system' as const, content: 'Hello' }], + }; + const yaml = toYaml(prompt); + expect(yaml).toContain('name: Test'); + expect(yaml).toContain('role: system'); + }); + + it('should handle multiline content', () => { + const prompt = { + messages: [{ role: 'system' as const, content: 'Line 1\nLine 2' }], + }; + const yaml = toYaml(prompt); + expect(yaml).toContain('content: |'); + }); + + it('should include description if present', () => { + const prompt = { + name: 'Test', + description: 'A test prompt', + messages: [{ role: 'system' as const, content: 'Hello' }], + }; + const yaml = toYaml(prompt); + expect(yaml).toContain('description: A test prompt'); + }); + }); + + describe('toJson', () => { + it('should serialize prompt to JSON', () => { + const prompt = { + name: 'Test', + messages: [{ role: 'system' as const, content: 'Hello' }], + }; + const json = toJson(prompt); + expect(() => JSON.parse(json)).not.toThrow(); + expect(JSON.parse(json).name).toBe('Test'); + }); + + it('should support non-pretty output', () => { + const prompt = { + name: 'Test', + messages: [{ role: 'system' as const, content: 'Hello' }], + }; + const json = toJson(prompt, false); + expect(json).not.toContain('\n'); + }); + }); + + describe('getSystemPrompt', () => { + it('should extract system message content', () => { + const prompt = { + messages: [ + { role: 'system' as const, content: 'System content' }, + { role: 'user' as const, content: 'User content' }, + ], + }; + expect(getSystemPrompt(prompt)).toBe('System content'); + }); + + it('should return empty string if no system message', () => { + const prompt = { + messages: [{ role: 'user' as const, content: 'Hello' }], + }; + expect(getSystemPrompt(prompt)).toBe(''); + }); + }); + + describe('interpolate', () => { + it('should replace variables with values', () => { + const prompt = { + messages: [{ role: 'system' as const, content: 'Hello {{name}}!' }], + }; + const result = interpolate(prompt, { name: 'World' }); + expect(result.messages[0].content).toBe('Hello World!'); + }); + + it('should use default values from variables definition', () => { + const prompt = { + messages: [{ role: 'system' as const, content: 'Hello {{name}}!' }], + variables: { name: { default: 'Guest' } }, + }; + const result = interpolate(prompt, {}); + expect(result.messages[0].content).toBe('Hello Guest!'); + }); + + it('should keep placeholder if no value or default', () => { + const prompt = { + messages: [{ role: 'system' as const, content: 'Hello {{name}}!' }], + }; + const result = interpolate(prompt, {}); + expect(result.messages[0].content).toBe('Hello {{name}}!'); + }); + + it('should interpolate multiple variables', () => { + const prompt = { + messages: [{ role: 'system' as const, content: '{{greeting}} {{name}}!' }], + }; + const result = interpolate(prompt, { greeting: 'Hello', name: 'World' }); + expect(result.messages[0].content).toBe('Hello World!'); + }); + }); +}); diff --git a/packages/prompts.chat/src/__tests__/quality.test.ts b/packages/prompts.chat/src/__tests__/quality.test.ts new file mode 100644 index 00000000..d8431eac --- /dev/null +++ b/packages/prompts.chat/src/__tests__/quality.test.ts @@ -0,0 +1,117 @@ +import { describe, it, expect } from 'vitest'; +import { + check, + validate, + isValid, + getSuggestions, +} from '../quality'; + +describe('quality module', () => { + describe('check', () => { + it('should return valid for a good prompt', () => { + const result = check('Act as a helpful assistant. Your task is to answer questions clearly and concisely.'); + expect(result.valid).toBe(true); + expect(result.score).toBeGreaterThan(0.5); + }); + + it('should return invalid for empty prompt', () => { + const result = check(''); + expect(result.valid).toBe(false); + expect(result.issues.some(i => i.code === 'EMPTY')).toBe(true); + }); + + it('should return invalid for too short prompt', () => { + const result = check('Hi'); + expect(result.valid).toBe(false); + expect(result.issues.some(i => i.code === 'TOO_SHORT')).toBe(true); + }); + + it('should detect gibberish', () => { + const result = check('qwertyuiop asdfghjkl'); + expect(result.issues.some(i => i.code === 'GIBBERISH')).toBe(true); + }); + + it('should detect role pattern', () => { + const result = check('Act as a developer. Help me write code.'); + expect(result.stats.hasRole).toBe(true); + }); + + it('should detect task pattern', () => { + const result = check('You should analyze this data and provide insights.'); + expect(result.stats.hasTask).toBe(true); + }); + + it('should detect constraints', () => { + const result = check('Do not use technical jargon. Always be concise.'); + expect(result.stats.hasConstraints).toBe(true); + }); + + it('should detect examples', () => { + const result = check('For example, if the input is "hello", output "world".'); + expect(result.stats.hasExamples).toBe(true); + }); + + it('should count variables', () => { + const result = check('Hello ${name}, your order ${orderId} is ready.'); + expect(result.stats.variableCount).toBe(2); + }); + + it('should detect unbalanced brackets', () => { + const result = check('This has unbalanced {brackets'); + expect(result.issues.some(i => i.code === 'UNBALANCED_BRACKETS')).toBe(true); + }); + + it('should calculate character count', () => { + const result = check('Hello World'); + expect(result.stats.characterCount).toBe(11); + }); + + it('should calculate word count', () => { + const result = check('Hello World Foo Bar'); + expect(result.stats.wordCount).toBe(4); + }); + }); + + describe('validate', () => { + it('should not throw for valid prompt', () => { + expect(() => validate('Act as a helpful assistant. Your task is to help.')).not.toThrow(); + }); + + it('should throw for invalid prompt', () => { + expect(() => validate('')).toThrow('Invalid prompt'); + }); + + it('should include error message in exception', () => { + expect(() => validate('')).toThrow('empty'); + }); + }); + + describe('isValid', () => { + it('should return true for valid prompt', () => { + expect(isValid('Act as a helpful assistant with clear instructions.')).toBe(true); + }); + + it('should return false for invalid prompt', () => { + expect(isValid('')).toBe(false); + }); + }); + + describe('getSuggestions', () => { + it('should return suggestions array', () => { + const suggestions = getSuggestions('Hello World'); + expect(Array.isArray(suggestions)).toBe(true); + }); + + it('should suggest adding role for prompts without role', () => { + const suggestions = getSuggestions('Analyze this data and give me results. Be thorough.'); + expect(suggestions.some(s => s.toLowerCase().includes('role'))).toBe(true); + }); + + it('should suggest adding variables for longer prompts', () => { + // Needs to be >30 words to trigger variable suggestion + const longPrompt = 'Analyze this data and give me results. Be thorough in your analysis. Consider all aspects of the problem carefully. Provide detailed and comprehensive analysis of the situation. Make sure to include actionable recommendations for improvement. Include specific examples and case studies where relevant.'; + const suggestions = getSuggestions(longPrompt); + expect(suggestions.some(s => s.toLowerCase().includes('variable'))).toBe(true); + }); + }); +}); diff --git a/packages/prompts.chat/src/__tests__/similarity.test.ts b/packages/prompts.chat/src/__tests__/similarity.test.ts new file mode 100644 index 00000000..a91bbe97 --- /dev/null +++ b/packages/prompts.chat/src/__tests__/similarity.test.ts @@ -0,0 +1,178 @@ +import { describe, it, expect } from 'vitest'; +import { + normalizeContent, + calculateSimilarity, + calculate, + isSimilarContent, + isDuplicate, + getContentFingerprint, + findDuplicates, + deduplicate, +} from '../similarity'; + +describe('similarity module', () => { + describe('normalizeContent', () => { + it('should convert to lowercase', () => { + expect(normalizeContent('Hello World')).toBe('hello world'); + }); + + it('should remove variables', () => { + expect(normalizeContent('Hello ${name}')).toBe('hello'); + }); + + it('should remove bracket placeholders', () => { + expect(normalizeContent('Hello [NAME]')).toBe('hello'); + }); + + it('should remove angle bracket placeholders', () => { + expect(normalizeContent('Hello ')).toBe('hello'); + }); + + it('should remove punctuation', () => { + expect(normalizeContent('Hello, World!')).toBe('hello world'); + }); + + it('should normalize whitespace', () => { + expect(normalizeContent('Hello World')).toBe('hello world'); + }); + + it('should handle empty string', () => { + expect(normalizeContent('')).toBe(''); + }); + }); + + describe('calculateSimilarity', () => { + it('should return 1 for identical strings', () => { + expect(calculateSimilarity('Hello World', 'Hello World')).toBe(1); + }); + + it('should return 1 for strings identical after normalization', () => { + expect(calculateSimilarity('Hello World!', 'hello world')).toBe(1); + }); + + it('should return 0 for completely different strings', () => { + const similarity = calculateSimilarity('abc', 'xyz'); + expect(similarity).toBeLessThan(0.2); + }); + + it('should return high similarity for similar strings', () => { + const similarity = calculateSimilarity( + 'You are a helpful assistant', + 'You are a helpful AI assistant' + ); + expect(similarity).toBeGreaterThan(0.7); + }); + + it('should handle empty strings', () => { + expect(calculateSimilarity('', '')).toBe(1); + expect(calculateSimilarity('hello', '')).toBe(0); + expect(calculateSimilarity('', 'hello')).toBe(0); + }); + + it('calculate alias should work the same', () => { + expect(calculate('a', 'b')).toBe(calculateSimilarity('a', 'b')); + }); + }); + + describe('isSimilarContent', () => { + it('should return true for similar content above threshold', () => { + expect(isSimilarContent('Hello World', 'Hello World!')).toBe(true); + }); + + it('should return false for different content', () => { + expect(isSimilarContent('Hello', 'Goodbye')).toBe(false); + }); + + it('should respect custom threshold', () => { + const content1 = 'Hello World'; + const content2 = 'Hello Earth'; + // These are somewhat similar but not 90% similar + expect(isSimilarContent(content1, content2, 0.9)).toBe(false); + expect(isSimilarContent(content1, content2, 0.3)).toBe(true); + }); + + it('isDuplicate alias should work the same', () => { + expect(isDuplicate('a', 'a')).toBe(isSimilarContent('a', 'a')); + }); + }); + + describe('getContentFingerprint', () => { + it('should return normalized content', () => { + const fingerprint = getContentFingerprint('Hello World!'); + expect(fingerprint).toBe('hello world'); + }); + + it('should truncate long content', () => { + const longContent = 'a'.repeat(1000); + const fingerprint = getContentFingerprint(longContent); + expect(fingerprint.length).toBeLessThanOrEqual(500); + }); + }); + + describe('findDuplicates', () => { + it('should find groups of similar prompts', () => { + const prompts = [ + { content: 'Hello World', id: 1 }, + { content: 'Hello World!', id: 2 }, + { content: 'Goodbye', id: 3 }, + { content: 'Goodbye!', id: 4 }, + ]; + + const groups = findDuplicates(prompts); + expect(groups).toHaveLength(2); + }); + + it('should return empty array when no duplicates', () => { + const prompts = [ + { content: 'Hello', id: 1 }, + { content: 'World', id: 2 }, + { content: 'Foo', id: 3 }, + ]; + + const groups = findDuplicates(prompts); + expect(groups).toHaveLength(0); + }); + + it('should respect custom threshold', () => { + const prompts = [ + { content: 'Hello World', id: 1 }, + { content: 'Hello Earth', id: 2 }, + ]; + + // With high threshold, should not be duplicates + expect(findDuplicates(prompts, 0.95)).toHaveLength(0); + // With low threshold, should be duplicates + expect(findDuplicates(prompts, 0.3)).toHaveLength(1); + }); + }); + + describe('deduplicate', () => { + it('should remove duplicate prompts keeping first occurrence', () => { + const prompts = [ + { content: 'Hello World', id: 1 }, + { content: 'Hello World!', id: 2 }, + { content: 'Goodbye', id: 3 }, + ]; + + const deduped = deduplicate(prompts); + expect(deduped).toHaveLength(2); + expect(deduped[0].id).toBe(1); + expect(deduped[1].id).toBe(3); + }); + + it('should keep all unique prompts', () => { + const prompts = [ + { content: 'Hello', id: 1 }, + { content: 'World', id: 2 }, + { content: 'Foo', id: 3 }, + ]; + + const deduped = deduplicate(prompts); + expect(deduped).toHaveLength(3); + }); + + it('should handle empty array', () => { + expect(deduplicate([])).toEqual([]); + }); + }); +}); diff --git a/packages/prompts.chat/src/__tests__/variables.test.ts b/packages/prompts.chat/src/__tests__/variables.test.ts new file mode 100644 index 00000000..8057de22 --- /dev/null +++ b/packages/prompts.chat/src/__tests__/variables.test.ts @@ -0,0 +1,192 @@ +import { describe, it, expect } from 'vitest'; +import { + detectVariables, + convertToSupportedFormat, + convertAllVariables, + normalize, + detect, + extractVariables, + compile, + getPatternDescription, +} from '../variables'; + +describe('variables module', () => { + describe('detectVariables', () => { + it('should detect double bracket variables', () => { + const text = 'Hello [[name]], your age is [[age]]'; + const vars = detectVariables(text); + expect(vars).toHaveLength(2); + expect(vars.map(v => v.name)).toEqual(['name', 'age']); + }); + + it('should detect double curly variables', () => { + const text = 'Hello {{name}}, your age is {{age}}'; + const vars = detectVariables(text); + expect(vars).toHaveLength(2); + expect(vars.map(v => v.name)).toEqual(['name', 'age']); + }); + + it('should detect single bracket uppercase variables', () => { + const text = 'Hello [NAME], your [AGE] is here'; + const vars = detectVariables(text); + expect(vars).toHaveLength(2); + expect(vars.map(v => v.name)).toEqual(['NAME', 'AGE']); + }); + + it('should detect angle bracket variables', () => { + const text = 'Hello , your is here'; + const vars = detectVariables(text); + expect(vars).toHaveLength(2); + expect(vars.map(v => v.name)).toEqual(['NAME', 'AGE']); + }); + + it('should detect percent variables', () => { + const text = 'Hello %name%, your %age% is here'; + const vars = detectVariables(text); + expect(vars).toHaveLength(2); + expect(vars.map(v => v.name)).toEqual(['name', 'age']); + }); + + it('should detect variables with defaults', () => { + const text = 'Hello [[name: World]]'; + const vars = detectVariables(text); + expect(vars).toHaveLength(1); + expect(vars[0].name).toBe('name'); + expect(vars[0].defaultValue).toBe('World'); + }); + + it('should handle empty text', () => { + const vars = detectVariables(''); + expect(vars).toHaveLength(0); + }); + + it('should not detect false positives like HTML tags', () => { + const text = 'Use
and tags'; + const vars = detectVariables(text); + expect(vars).toHaveLength(0); + }); + + it('detect alias should work the same as detectVariables', () => { + const text = 'Hello [[name]]'; + expect(detect(text)).toEqual(detectVariables(text)); + }); + }); + + describe('convertToSupportedFormat', () => { + it('should convert variable to ${name} format', () => { + const variable = { + original: '[[name]]', + name: 'name', + pattern: 'double_bracket' as const, + startIndex: 0, + endIndex: 8, + }; + expect(convertToSupportedFormat(variable)).toBe('${name}'); + }); + + it('should include default value if present', () => { + const variable = { + original: '[[name: World]]', + name: 'name', + defaultValue: 'World', + pattern: 'double_bracket' as const, + startIndex: 0, + endIndex: 15, + }; + expect(convertToSupportedFormat(variable)).toBe('${name:World}'); + }); + + it('should normalize names with spaces to underscores', () => { + const variable = { + original: '[[User Name]]', + name: 'User Name', + pattern: 'double_bracket' as const, + startIndex: 0, + endIndex: 13, + }; + expect(convertToSupportedFormat(variable)).toBe('${user_name}'); + }); + }); + + describe('convertAllVariables / normalize', () => { + it('should convert all variable formats to ${name}', () => { + const text = 'Hello [[name]], {{age}}, [CITY]'; + const normalized = convertAllVariables(text); + expect(normalized).toContain('${name}'); + expect(normalized).toContain('${age}'); + expect(normalized).toContain('${city}'); + }); + + it('should preserve defaults in normalization', () => { + const text = 'Hello [[name: World]]'; + const normalized = normalize(text); + expect(normalized).toBe('Hello ${name:World}'); + }); + + it('should handle text with no variables', () => { + const text = 'Hello World'; + expect(convertAllVariables(text)).toBe('Hello World'); + }); + }); + + describe('extractVariables', () => { + it('should extract variables from ${var} format', () => { + const text = 'Hello ${name}, you are ${age} years old'; + const vars = extractVariables(text); + expect(vars).toHaveLength(2); + expect(vars[0].name).toBe('name'); + expect(vars[1].name).toBe('age'); + }); + + it('should extract default values', () => { + const text = 'Hello ${name:World}'; + const vars = extractVariables(text); + expect(vars).toHaveLength(1); + expect(vars[0].name).toBe('name'); + expect(vars[0].defaultValue).toBe('World'); + }); + + it('should return empty array for no variables', () => { + const vars = extractVariables('Hello World'); + expect(vars).toHaveLength(0); + }); + }); + + describe('compile', () => { + it('should replace variables with values', () => { + const text = 'Hello ${name}, you are ${age} years old'; + const result = compile(text, { name: 'John', age: '30' }); + expect(result).toBe('Hello John, you are 30 years old'); + }); + + it('should use default values when not provided', () => { + const text = 'Hello ${name:World}'; + const result = compile(text, {}); + expect(result).toBe('Hello World'); + }); + + it('should keep original when no value and useDefaults is false', () => { + const text = 'Hello ${name:World}'; + const result = compile(text, {}, { useDefaults: false }); + expect(result).toBe('Hello ${name:World}'); + }); + + it('should handle missing variables without defaults', () => { + const text = 'Hello ${name}'; + const result = compile(text, {}); + expect(result).toBe('Hello ${name}'); + }); + }); + + describe('getPatternDescription', () => { + it('should return description for each pattern', () => { + expect(getPatternDescription('double_bracket')).toBe('[[...]]'); + expect(getPatternDescription('double_curly')).toBe('{{...}}'); + expect(getPatternDescription('single_bracket')).toBe('[...]'); + expect(getPatternDescription('single_curly')).toBe('{...}'); + expect(getPatternDescription('angle_bracket')).toBe('<...>'); + expect(getPatternDescription('percent')).toBe('%...%'); + expect(getPatternDescription('dollar_curly')).toBe('${...}'); + }); + }); +}); diff --git a/packages/prompts.chat/src/builder/audio.ts b/packages/prompts.chat/src/builder/audio.ts new file mode 100644 index 00000000..c5ff781f --- /dev/null +++ b/packages/prompts.chat/src/builder/audio.ts @@ -0,0 +1,724 @@ +/** + * Audio/Music Prompt Builder - Comprehensive music generation prompt builder + * + * Based on Suno, Udio, and other music generation best practices. + * + * @example + * ```ts + * import { audio } from 'prompts.chat/builder'; + * + * const prompt = audio() + * .genre("synthwave") + * .mood("nostalgic", "dreamy") + * .tempo(110) + * .instruments(["synthesizer", "drums", "bass"]) + * .structure({ intro: 8, verse: 16, chorus: 16 }) + * .build(); + * ``` + */ + +import type { Mood, OutputFormat } from './media'; + +// ============================================================================ +// AUDIO-SPECIFIC TYPES +// ============================================================================ + +export type MusicGenre = + | 'pop' | 'rock' | 'jazz' | 'classical' | 'electronic' | 'hip-hop' | 'r&b' + | 'country' | 'folk' | 'blues' | 'metal' | 'punk' | 'indie' | 'alternative' + | 'ambient' | 'lo-fi' | 'synthwave' | 'orchestral' | 'cinematic' | 'world' + | 'latin' | 'reggae' | 'soul' | 'funk' | 'disco' | 'house' | 'techno' | 'edm' + | 'trap' | 'drill' | 'k-pop' | 'j-pop' | 'bossa-nova' | 'gospel' | 'grunge' + | 'shoegaze' | 'post-rock' | 'prog-rock' | 'psychedelic' | 'chillwave' + | 'vaporwave' | 'drum-and-bass' | 'dubstep' | 'trance' | 'hardcore'; + +export type Instrument = + | 'piano' | 'guitar' | 'acoustic-guitar' | 'electric-guitar' | 'bass' | 'drums' + | 'violin' | 'cello' | 'viola' | 'flute' | 'saxophone' | 'trumpet' | 'trombone' + | 'synthesizer' | 'organ' | 'harp' | 'percussion' | 'strings' | 'brass' | 'woodwinds' + | 'choir' | 'vocals' | 'beatbox' | 'turntables' | 'harmonica' | 'banjo' | 'ukulele' + | 'mandolin' | 'accordion' | 'marimba' | 'vibraphone' | 'xylophone' | 'timpani' + | 'congas' | 'bongos' | 'djembe' | 'tabla' | 'sitar' | 'erhu' | 'koto' + | '808' | '909' | 'moog' | 'rhodes' | 'wurlitzer' | 'mellotron' | 'theremin'; + +export type VocalStyle = + | 'male' | 'female' | 'duet' | 'choir' | 'a-cappella' | 'spoken-word' | 'rap' + | 'falsetto' | 'belting' | 'whisper' | 'growl' | 'melodic' | 'harmonized' + | 'auto-tuned' | 'operatic' | 'soul' | 'breathy' | 'nasal' | 'raspy' | 'clear'; + +export type VocalLanguage = + | 'english' | 'spanish' | 'french' | 'german' | 'italian' | 'portuguese' + | 'japanese' | 'korean' | 'chinese' | 'arabic' | 'hindi' | 'russian' | 'turkish' + | 'instrumental'; + +export type TempoMarking = + | 'largo' | 'adagio' | 'andante' | 'moderato' | 'allegro' | 'vivace' | 'presto'; + +export type TimeSignature = '4/4' | '3/4' | '6/8' | '2/4' | '5/4' | '7/8' | '12/8'; + +export type MusicalKey = + | 'C' | 'C#' | 'Db' | 'D' | 'D#' | 'Eb' | 'E' | 'F' | 'F#' | 'Gb' + | 'G' | 'G#' | 'Ab' | 'A' | 'A#' | 'Bb' | 'B' + | 'Cm' | 'C#m' | 'Dm' | 'D#m' | 'Ebm' | 'Em' | 'Fm' | 'F#m' + | 'Gm' | 'G#m' | 'Am' | 'A#m' | 'Bbm' | 'Bm'; + +export type SongSection = + | 'intro' | 'verse' | 'pre-chorus' | 'chorus' | 'bridge' | 'breakdown' + | 'drop' | 'build-up' | 'outro' | 'solo' | 'interlude' | 'hook'; + +export type ProductionStyle = + | 'lo-fi' | 'hi-fi' | 'vintage' | 'modern' | 'polished' | 'raw' | 'organic' + | 'synthetic' | 'acoustic' | 'electric' | 'hybrid' | 'minimalist' | 'maximalist' + | 'layered' | 'sparse' | 'dense' | 'atmospheric' | 'punchy' | 'warm' | 'bright'; + +export type Era = + | '1950s' | '1960s' | '1970s' | '1980s' | '1990s' | '2000s' | '2010s' | '2020s' + | 'retro' | 'vintage' | 'classic' | 'modern' | 'futuristic'; + +// ============================================================================ +// AUDIO INTERFACES +// ============================================================================ + +export interface AudioGenre { + primary: MusicGenre; + secondary?: MusicGenre[]; + subgenre?: string; + fusion?: string[]; +} + +export interface AudioMood { + primary: Mood | string; + secondary?: (Mood | string)[]; + energy?: 'low' | 'medium' | 'high' | 'building' | 'fluctuating'; + emotion?: string; +} + +export interface AudioTempo { + bpm?: number; + marking?: TempoMarking; + feel?: 'steady' | 'swung' | 'shuffled' | 'syncopated' | 'rubato' | 'driving'; + variation?: boolean; +} + +export interface AudioVocals { + style?: VocalStyle | VocalStyle[]; + language?: VocalLanguage; + lyrics?: string; + theme?: string; + delivery?: string; + harmonies?: boolean; + adlibs?: boolean; +} + +export interface AudioInstrumentation { + lead?: Instrument | Instrument[]; + rhythm?: Instrument | Instrument[]; + bass?: Instrument; + percussion?: Instrument | Instrument[]; + pads?: Instrument | Instrument[]; + effects?: string[]; + featured?: Instrument; +} + +export interface AudioStructure { + sections?: Array<{ + type: SongSection; + bars?: number; + description?: string; + }>; + intro?: number; + verse?: number; + chorus?: number; + bridge?: number; + outro?: number; + form?: string; + duration?: number; +} + +export interface AudioProduction { + style?: ProductionStyle | ProductionStyle[]; + era?: Era; + reference?: string[]; + mix?: string; + mastering?: string; + effects?: string[]; + texture?: string; +} + +export interface AudioTechnical { + key?: MusicalKey; + timeSignature?: TimeSignature; + duration?: number; + format?: 'song' | 'instrumental' | 'jingle' | 'loop' | 'soundtrack'; +} + +export interface BuiltAudioPrompt { + prompt: string; + stylePrompt: string; + lyricsPrompt?: string; + structure: { + genre?: AudioGenre; + mood?: AudioMood; + tempo?: AudioTempo; + vocals?: AudioVocals; + instrumentation?: AudioInstrumentation; + structure?: AudioStructure; + production?: AudioProduction; + technical?: AudioTechnical; + tags?: string[]; + }; +} + +// ============================================================================ +// AUDIO PROMPT BUILDER +// ============================================================================ + +export class AudioPromptBuilder { + private _genre?: AudioGenre; + private _mood?: AudioMood; + private _tempo?: AudioTempo; + private _vocals?: AudioVocals; + private _instrumentation?: AudioInstrumentation; + private _structure?: AudioStructure; + private _production?: AudioProduction; + private _technical?: AudioTechnical; + private _tags: string[] = []; + private _custom: string[] = []; + + // --- Genre Methods --- + + genre(primary: MusicGenre | AudioGenre): this { + if (typeof primary === 'string') { + this._genre = { ...(this._genre || { primary: 'pop' }), primary }; + } else { + this._genre = { ...(this._genre || { primary: 'pop' }), ...primary }; + } + return this; + } + + subgenre(subgenre: string): this { + this._genre = { ...(this._genre || { primary: 'pop' }), subgenre }; + return this; + } + + fusion(genres: MusicGenre[]): this { + this._genre = { + ...(this._genre || { primary: 'pop' }), + secondary: genres, + fusion: genres as string[], + }; + return this; + } + + // --- Mood Methods --- + + mood(primary: Mood | string, ...secondary: (Mood | string)[]): this { + this._mood = { + primary, + secondary: secondary.length ? secondary : undefined, + }; + return this; + } + + energy(level: AudioMood['energy']): this { + this._mood = { ...(this._mood || { primary: 'energetic' }), energy: level }; + return this; + } + + emotion(emotion: string): this { + this._mood = { ...(this._mood || { primary: 'emotional' }), emotion }; + return this; + } + + // --- Tempo Methods --- + + tempo(bpmOrSettings: number | AudioTempo): this { + if (typeof bpmOrSettings === 'number') { + this._tempo = { ...(this._tempo || {}), bpm: bpmOrSettings }; + } else { + this._tempo = { ...(this._tempo || {}), ...bpmOrSettings }; + } + return this; + } + + bpm(bpm: number): this { + this._tempo = { ...(this._tempo || {}), bpm }; + return this; + } + + tempoMarking(marking: TempoMarking): this { + this._tempo = { ...(this._tempo || {}), marking }; + return this; + } + + tempoFeel(feel: AudioTempo['feel']): this { + this._tempo = { ...(this._tempo || {}), feel }; + return this; + } + + // --- Vocal Methods --- + + vocals(settings: AudioVocals): this { + this._vocals = { ...(this._vocals || {}), ...settings }; + return this; + } + + vocalStyle(style: VocalStyle | VocalStyle[]): this { + this._vocals = { ...(this._vocals || {}), style }; + return this; + } + + language(language: VocalLanguage): this { + this._vocals = { ...(this._vocals || {}), language }; + return this; + } + + lyrics(lyrics: string): this { + this._vocals = { ...(this._vocals || {}), lyrics }; + return this; + } + + lyricsTheme(theme: string): this { + this._vocals = { ...(this._vocals || {}), theme }; + return this; + } + + delivery(delivery: string): this { + this._vocals = { ...(this._vocals || {}), delivery }; + return this; + } + + instrumental(): this { + this._vocals = { ...(this._vocals || {}), language: 'instrumental' }; + return this; + } + + // --- Instrumentation Methods --- + + instruments(instruments: Instrument[]): this { + this._instrumentation = { + ...(this._instrumentation || {}), + lead: instruments, + }; + return this; + } + + instrumentation(settings: AudioInstrumentation): this { + this._instrumentation = { ...(this._instrumentation || {}), ...settings }; + return this; + } + + leadInstrument(instrument: Instrument | Instrument[]): this { + this._instrumentation = { ...(this._instrumentation || {}), lead: instrument }; + return this; + } + + rhythmSection(instruments: Instrument[]): this { + this._instrumentation = { ...(this._instrumentation || {}), rhythm: instruments }; + return this; + } + + bassInstrument(instrument: Instrument): this { + this._instrumentation = { ...(this._instrumentation || {}), bass: instrument }; + return this; + } + + percussion(instruments: Instrument | Instrument[]): this { + this._instrumentation = { ...(this._instrumentation || {}), percussion: instruments }; + return this; + } + + pads(instruments: Instrument | Instrument[]): this { + this._instrumentation = { ...(this._instrumentation || {}), pads: instruments }; + return this; + } + + featuredInstrument(instrument: Instrument): this { + this._instrumentation = { ...(this._instrumentation || {}), featured: instrument }; + return this; + } + + // --- Structure Methods --- + + structure(settings: AudioStructure | { [key in SongSection]?: number }): this { + if ('sections' in settings || 'form' in settings || 'duration' in settings) { + this._structure = { ...(this._structure || {}), ...settings as AudioStructure }; + } else { + // Convert shorthand to full structure + const sections: AudioStructure['sections'] = []; + for (const [type, bars] of Object.entries(settings)) { + if (typeof bars === 'number') { + sections.push({ type: type as SongSection, bars }); + } + } + this._structure = { ...(this._structure || {}), sections }; + } + return this; + } + + section(type: SongSection, bars?: number, description?: string): this { + const sections = this._structure?.sections || []; + sections.push({ type, bars, description }); + this._structure = { ...(this._structure || {}), sections }; + return this; + } + + form(form: string): this { + this._structure = { ...(this._structure || {}), form }; + return this; + } + + duration(seconds: number): this { + this._structure = { ...(this._structure || {}), duration: seconds }; + return this; + } + + // --- Production Methods --- + + production(settings: AudioProduction): this { + this._production = { ...(this._production || {}), ...settings }; + return this; + } + + productionStyle(style: ProductionStyle | ProductionStyle[]): this { + this._production = { ...(this._production || {}), style }; + return this; + } + + era(era: Era): this { + this._production = { ...(this._production || {}), era }; + return this; + } + + reference(artists: string[]): this { + this._production = { ...(this._production || {}), reference: artists }; + return this; + } + + texture(texture: string): this { + this._production = { ...(this._production || {}), texture }; + return this; + } + + effects(effects: string[]): this { + this._production = { ...(this._production || {}), effects }; + return this; + } + + // --- Technical Methods --- + + technical(settings: AudioTechnical): this { + this._technical = { ...(this._technical || {}), ...settings }; + return this; + } + + key(key: MusicalKey): this { + this._technical = { ...(this._technical || {}), key }; + return this; + } + + timeSignature(sig: TimeSignature): this { + this._technical = { ...(this._technical || {}), timeSignature: sig }; + return this; + } + + formatType(format: AudioTechnical['format']): this { + this._technical = { ...(this._technical || {}), format }; + return this; + } + + // --- Tags & Custom --- + + tag(tag: string): this { + this._tags.push(tag); + return this; + } + + tags(tags: string[]): this { + this._tags = [...this._tags, ...tags]; + return this; + } + + custom(text: string): this { + this._custom.push(text); + return this; + } + + // --- Build Methods --- + + private buildStylePrompt(): string { + const parts: string[] = []; + + // Genre + if (this._genre) { + let genreText: string = this._genre.primary; + if (this._genre.subgenre) genreText = `${this._genre.subgenre} ${genreText}`; + if (this._genre.secondary?.length) { + genreText += ` with ${this._genre.secondary.join(' and ')} influences`; + } + parts.push(genreText); + } + + // Mood + if (this._mood) { + let moodText = String(this._mood.primary); + if (this._mood.secondary?.length) { + moodText += `, ${this._mood.secondary.join(', ')}`; + } + if (this._mood.energy) moodText += `, ${this._mood.energy} energy`; + parts.push(moodText); + } + + // Tempo + if (this._tempo) { + const tempoParts: string[] = []; + if (this._tempo.bpm) tempoParts.push(`${this._tempo.bpm} BPM`); + if (this._tempo.marking) tempoParts.push(this._tempo.marking); + if (this._tempo.feel) tempoParts.push(`${this._tempo.feel} feel`); + if (tempoParts.length) parts.push(tempoParts.join(', ')); + } + + // Instrumentation + if (this._instrumentation) { + const instrParts: string[] = []; + if (this._instrumentation.lead) { + const leads = Array.isArray(this._instrumentation.lead) + ? this._instrumentation.lead : [this._instrumentation.lead]; + instrParts.push(leads.join(', ')); + } + if (this._instrumentation.featured) { + instrParts.push(`featuring ${this._instrumentation.featured}`); + } + if (instrParts.length) parts.push(instrParts.join(', ')); + } + + // Vocals + if (this._vocals) { + const vocalParts: string[] = []; + if (this._vocals.language === 'instrumental') { + vocalParts.push('instrumental'); + } else { + if (this._vocals.style) { + const styles = Array.isArray(this._vocals.style) + ? this._vocals.style : [this._vocals.style]; + vocalParts.push(`${styles.join(' and ')} vocals`); + } + if (this._vocals.language && this._vocals.language !== 'english') { + vocalParts.push(`in ${this._vocals.language}`); + } + } + if (vocalParts.length) parts.push(vocalParts.join(' ')); + } + + // Production + if (this._production) { + const prodParts: string[] = []; + if (this._production.style) { + const styles = Array.isArray(this._production.style) + ? this._production.style : [this._production.style]; + prodParts.push(`${styles.join(', ')} production`); + } + if (this._production.era) prodParts.push(`${this._production.era} sound`); + if (this._production.texture) prodParts.push(this._production.texture); + if (prodParts.length) parts.push(prodParts.join(', ')); + } + + // Technical + if (this._technical) { + const techParts: string[] = []; + if (this._technical.key) techParts.push(`in the key of ${this._technical.key}`); + if (this._technical.timeSignature && this._technical.timeSignature !== '4/4') { + techParts.push(`${this._technical.timeSignature} time`); + } + if (techParts.length) parts.push(techParts.join(', ')); + } + + // Tags + if (this._tags.length) { + parts.push(this._tags.join(', ')); + } + + // Custom + if (this._custom.length) { + parts.push(this._custom.join(', ')); + } + + return parts.join(', '); + } + + private buildLyricsPrompt(): string | undefined { + if (!this._vocals?.lyrics && !this._vocals?.theme) return undefined; + + const parts: string[] = []; + + if (this._vocals.theme) { + parts.push(`Theme: ${this._vocals.theme}`); + } + + if (this._vocals.lyrics) { + parts.push(this._vocals.lyrics); + } + + return parts.join('\n\n'); + } + + private buildFullPrompt(): string { + const sections: string[] = []; + + sections.push(`Style: ${this.buildStylePrompt()}`); + + if (this._structure?.sections?.length) { + const structureText = this._structure.sections + .map(s => `[${s.type.toUpperCase()}]${s.description ? ` ${s.description}` : ''}`) + .join('\n'); + sections.push(`Structure:\n${structureText}`); + } + + const lyrics = this.buildLyricsPrompt(); + if (lyrics) { + sections.push(`Lyrics:\n${lyrics}`); + } + + return sections.join('\n\n'); + } + + build(): BuiltAudioPrompt { + return { + prompt: this.buildFullPrompt(), + stylePrompt: this.buildStylePrompt(), + lyricsPrompt: this.buildLyricsPrompt(), + structure: { + genre: this._genre, + mood: this._mood, + tempo: this._tempo, + vocals: this._vocals, + instrumentation: this._instrumentation, + structure: this._structure, + production: this._production, + technical: this._technical, + tags: this._tags.length ? this._tags : undefined, + }, + }; + } + + toString(): string { + return this.build().prompt; + } + + toStyleString(): string { + return this.build().stylePrompt; + } + + toJSON(): string { + return JSON.stringify(this.build().structure, null, 2); + } + + toYAML(): string { + return objectToYaml(this.build().structure); + } + + toMarkdown(): string { + const built = this.build(); + const sections: string[] = ['# Audio Prompt\n']; + + sections.push('## Style Prompt\n```\n' + built.stylePrompt + '\n```\n'); + + if (built.lyricsPrompt) { + sections.push('## Lyrics\n```\n' + built.lyricsPrompt + '\n```\n'); + } + + if (built.structure.genre) { + sections.push('## Genre\n' + objectToMarkdownList(built.structure.genre)); + } + if (built.structure.mood) { + sections.push('## Mood\n' + objectToMarkdownList(built.structure.mood)); + } + if (built.structure.tempo) { + sections.push('## Tempo\n' + objectToMarkdownList(built.structure.tempo)); + } + if (built.structure.vocals) { + sections.push('## Vocals\n' + objectToMarkdownList(built.structure.vocals)); + } + if (built.structure.instrumentation) { + sections.push('## Instrumentation\n' + objectToMarkdownList(built.structure.instrumentation)); + } + if (built.structure.production) { + sections.push('## Production\n' + objectToMarkdownList(built.structure.production)); + } + + return sections.join('\n'); + } + + outputFormat(fmt: OutputFormat): string { + switch (fmt) { + case 'json': return this.toJSON(); + case 'yaml': return this.toYAML(); + case 'markdown': return this.toMarkdown(); + default: return this.toString(); + } + } +} + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +function objectToYaml(obj: object, indent = 0): string { + const spaces = ' '.repeat(indent); + const lines: string[] = []; + + for (const [key, value] of Object.entries(obj)) { + if (value === undefined || value === null) continue; + + if (Array.isArray(value)) { + if (value.length === 0) continue; + lines.push(`${spaces}${key}:`); + for (const item of value) { + if (typeof item === 'object') { + lines.push(`${spaces} -`); + lines.push(objectToYaml(item, indent + 2).replace(/^/gm, ' ')); + } else { + lines.push(`${spaces} - ${item}`); + } + } + } else if (typeof value === 'object') { + lines.push(`${spaces}${key}:`); + lines.push(objectToYaml(value, indent + 1)); + } else { + lines.push(`${spaces}${key}: ${value}`); + } + } + + return lines.join('\n'); +} + +function objectToMarkdownList(obj: object, indent = 0): string { + const spaces = ' '.repeat(indent); + const lines: string[] = []; + + for (const [key, value] of Object.entries(obj)) { + if (value === undefined || value === null) continue; + + if (Array.isArray(value)) { + lines.push(`${spaces}- **${key}:** ${value.join(', ')}`); + } else if (typeof value === 'object') { + lines.push(`${spaces}- **${key}:**`); + lines.push(objectToMarkdownList(value, indent + 1)); + } else { + lines.push(`${spaces}- **${key}:** ${value}`); + } + } + + return lines.join('\n'); +} + +// ============================================================================ +// FACTORY FUNCTION +// ============================================================================ + +/** + * Create a new audio/music prompt builder + */ +export function audio(): AudioPromptBuilder { + return new AudioPromptBuilder(); +} diff --git a/packages/prompts.chat/src/builder/chat.ts b/packages/prompts.chat/src/builder/chat.ts new file mode 100644 index 00000000..02ecdaaa --- /dev/null +++ b/packages/prompts.chat/src/builder/chat.ts @@ -0,0 +1,1071 @@ +/** + * Chat Prompt Builder - Model-Agnostic Conversation Prompt Builder + * + * Build structured prompts for any chat/conversation model. + * Focus on prompt engineering, not model-specific features. + * + * @example + * ```ts + * import { chat } from 'prompts.chat/builder'; + * + * const prompt = chat() + * .role("helpful coding assistant") + * .context("Building a React application") + * .task("Explain async/await in JavaScript") + * .stepByStep() + * .detailed() + * .build(); + * ``` + */ + +// ============================================================================ +// TYPES & INTERFACES +// ============================================================================ + +// --- Message Types --- +export type MessageRole = 'system' | 'user' | 'assistant'; + +export interface ChatMessage { + role: MessageRole; + content: string; + name?: string; +} + +// --- Response Format Types --- +export type ResponseFormatType = 'text' | 'json' | 'markdown' | 'code' | 'table'; + +export interface JsonSchema { + name: string; + description?: string; + schema: Record; +} + +export interface ResponseFormat { + type: ResponseFormatType; + jsonSchema?: JsonSchema; + language?: string; +} + +// --- Persona Types --- +export type PersonaTone = + | 'professional' | 'casual' | 'formal' | 'friendly' | 'academic' + | 'technical' | 'creative' | 'empathetic' | 'authoritative' | 'playful' + | 'concise' | 'detailed' | 'socratic' | 'coaching' | 'analytical' + | 'encouraging' | 'neutral' | 'humorous' | 'serious'; + +export type PersonaExpertise = + | 'general' | 'coding' | 'writing' | 'analysis' | 'research' + | 'teaching' | 'counseling' | 'creative' | 'legal' | 'medical' + | 'financial' | 'scientific' | 'engineering' | 'design' | 'marketing' + | 'business' | 'philosophy' | 'history' | 'languages' | 'mathematics'; + +// --- Reasoning Types --- +export type ReasoningStyle = + | 'step-by-step' | 'chain-of-thought' | 'tree-of-thought' + | 'direct' | 'analytical' | 'comparative' | 'deductive' | 'inductive' + | 'first-principles' | 'analogical' | 'devil-advocate'; + +// --- Output Types --- +export type OutputLength = 'brief' | 'moderate' | 'detailed' | 'comprehensive' | 'exhaustive'; +export type OutputStyle = 'prose' | 'bullet-points' | 'numbered-list' | 'table' | 'code' | 'mixed' | 'qa' | 'dialogue'; + +// ============================================================================ +// INTERFACES +// ============================================================================ + +export interface ChatPersona { + name?: string; + role?: string; + tone?: PersonaTone | PersonaTone[]; + expertise?: PersonaExpertise | PersonaExpertise[]; + personality?: string[]; + background?: string; + language?: string; + verbosity?: OutputLength; +} + +export interface ChatContext { + background?: string; + domain?: string; + audience?: string; + purpose?: string; + constraints?: string[]; + assumptions?: string[]; + knowledge?: string[]; +} + +export interface ChatTask { + instruction: string; + steps?: string[]; + deliverables?: string[]; + criteria?: string[]; + antiPatterns?: string[]; + priority?: 'accuracy' | 'speed' | 'creativity' | 'thoroughness'; +} + +export interface ChatOutput { + format?: ResponseFormat; + length?: OutputLength; + style?: OutputStyle; + language?: string; + includeExplanation?: boolean; + includeExamples?: boolean; + includeSources?: boolean; + includeConfidence?: boolean; +} + +export interface ChatReasoning { + style?: ReasoningStyle; + showWork?: boolean; + verifyAnswer?: boolean; + considerAlternatives?: boolean; + explainAssumptions?: boolean; +} + +export interface ChatExample { + input: string; + output: string; + explanation?: string; +} + +export interface ChatMemory { + summary?: string; + facts?: string[]; + preferences?: string[]; + history?: ChatMessage[]; +} + +export interface BuiltChatPrompt { + messages: ChatMessage[]; + systemPrompt: string; + userPrompt?: string; + metadata: { + persona?: ChatPersona; + context?: ChatContext; + task?: ChatTask; + output?: ChatOutput; + reasoning?: ChatReasoning; + examples?: ChatExample[]; + }; +} + +// ============================================================================ +// CHAT PROMPT BUILDER +// ============================================================================ + +export class ChatPromptBuilder { + private _messages: ChatMessage[] = []; + private _persona?: ChatPersona; + private _context?: ChatContext; + private _task?: ChatTask; + private _output?: ChatOutput; + private _reasoning?: ChatReasoning; + private _examples: ChatExample[] = []; + private _memory?: ChatMemory; + private _customSystemParts: string[] = []; + + // --- Message Methods --- + + system(content: string): this { + // Remove existing system message and add new one at beginning + this._messages = this._messages.filter(m => m.role !== 'system'); + this._messages.unshift({ role: 'system', content }); + return this; + } + + user(content: string, name?: string): this { + this._messages.push({ role: 'user', content, name }); + return this; + } + + assistant(content: string): this { + this._messages.push({ role: 'assistant', content }); + return this; + } + + message(role: MessageRole, content: string, name?: string): this { + this._messages.push({ role, content, name }); + return this; + } + + messages(messages: ChatMessage[]): this { + this._messages = [...this._messages, ...messages]; + return this; + } + + conversation(turns: Array<{ user: string; assistant?: string }>): this { + for (const turn of turns) { + this.user(turn.user); + if (turn.assistant) { + this.assistant(turn.assistant); + } + } + return this; + } + + // --- Persona Methods --- + + persona(settings: ChatPersona | string): this { + if (typeof settings === 'string') { + this._persona = { ...(this._persona || {}), role: settings }; + } else { + this._persona = { ...(this._persona || {}), ...settings }; + } + return this; + } + + role(role: string): this { + this._persona = { ...(this._persona || {}), role }; + return this; + } + + tone(tone: PersonaTone | PersonaTone[]): this { + this._persona = { ...(this._persona || {}), tone }; + return this; + } + + expertise(expertise: PersonaExpertise | PersonaExpertise[]): this { + this._persona = { ...(this._persona || {}), expertise }; + return this; + } + + personality(traits: string[]): this { + this._persona = { ...(this._persona || {}), personality: traits }; + return this; + } + + background(background: string): this { + this._persona = { ...(this._persona || {}), background }; + return this; + } + + speakAs(name: string): this { + this._persona = { ...(this._persona || {}), name }; + return this; + } + + responseLanguage(language: string): this { + this._persona = { ...(this._persona || {}), language }; + return this; + } + + // --- Context Methods --- + + context(settings: ChatContext | string): this { + if (typeof settings === 'string') { + this._context = { ...(this._context || {}), background: settings }; + } else { + this._context = { ...(this._context || {}), ...settings }; + } + return this; + } + + domain(domain: string): this { + this._context = { ...(this._context || {}), domain }; + return this; + } + + audience(audience: string): this { + this._context = { ...(this._context || {}), audience }; + return this; + } + + purpose(purpose: string): this { + this._context = { ...(this._context || {}), purpose }; + return this; + } + + constraints(constraints: string[]): this { + const existing = this._context?.constraints || []; + this._context = { ...(this._context || {}), constraints: [...existing, ...constraints] }; + return this; + } + + constraint(constraint: string): this { + return this.constraints([constraint]); + } + + assumptions(assumptions: string[]): this { + this._context = { ...(this._context || {}), assumptions }; + return this; + } + + knowledge(facts: string[]): this { + this._context = { ...(this._context || {}), knowledge: facts }; + return this; + } + + // --- Task Methods --- + + task(instruction: string | ChatTask): this { + if (typeof instruction === 'string') { + this._task = { ...(this._task || { instruction: '' }), instruction }; + } else { + this._task = { ...(this._task || { instruction: '' }), ...instruction }; + } + return this; + } + + instruction(instruction: string): this { + this._task = { ...(this._task || { instruction: '' }), instruction }; + return this; + } + + steps(steps: string[]): this { + this._task = { ...(this._task || { instruction: '' }), steps }; + return this; + } + + deliverables(deliverables: string[]): this { + this._task = { ...(this._task || { instruction: '' }), deliverables }; + return this; + } + + criteria(criteria: string[]): this { + this._task = { ...(this._task || { instruction: '' }), criteria }; + return this; + } + + avoid(antiPatterns: string[]): this { + this._task = { ...(this._task || { instruction: '' }), antiPatterns }; + return this; + } + + priority(priority: ChatTask['priority']): this { + this._task = { ...(this._task || { instruction: '' }), priority }; + return this; + } + + // --- Example Methods --- + + example(input: string, output: string, explanation?: string): this { + this._examples.push({ input, output, explanation }); + return this; + } + + examples(examples: ChatExample[]): this { + this._examples = [...this._examples, ...examples]; + return this; + } + + fewShot(examples: Array<{ input: string; output: string }>): this { + for (const ex of examples) { + this._examples.push(ex); + } + return this; + } + + // --- Output Methods --- + + output(settings: ChatOutput): this { + this._output = { ...(this._output || {}), ...settings }; + return this; + } + + outputFormat(format: ResponseFormatType): this { + this._output = { + ...(this._output || {}), + format: { type: format } + }; + return this; + } + + json(schema?: JsonSchema): this { + if (schema) { + this._output = { + ...(this._output || {}), + format: { type: 'json', jsonSchema: schema } + }; + } else { + this._output = { + ...(this._output || {}), + format: { type: 'json' } + }; + } + return this; + } + + jsonSchema(name: string, schema: Record, description?: string): this { + this._output = { + ...(this._output || {}), + format: { + type: 'json', + jsonSchema: { name, schema, description } + } + }; + return this; + } + + markdown(): this { + this._output = { ...(this._output || {}), format: { type: 'markdown' } }; + return this; + } + + code(language?: string): this { + this._output = { ...(this._output || {}), format: { type: 'code', language } }; + return this; + } + + table(): this { + this._output = { ...(this._output || {}), format: { type: 'table' } }; + return this; + } + + length(length: OutputLength): this { + this._output = { ...(this._output || {}), length }; + return this; + } + + style(style: OutputStyle): this { + this._output = { ...(this._output || {}), style }; + return this; + } + + brief(): this { + return this.length('brief'); + } + + moderate(): this { + return this.length('moderate'); + } + + detailed(): this { + return this.length('detailed'); + } + + comprehensive(): this { + return this.length('comprehensive'); + } + + exhaustive(): this { + return this.length('exhaustive'); + } + + withExamples(): this { + this._output = { ...(this._output || {}), includeExamples: true }; + return this; + } + + withExplanation(): this { + this._output = { ...(this._output || {}), includeExplanation: true }; + return this; + } + + withSources(): this { + this._output = { ...(this._output || {}), includeSources: true }; + return this; + } + + withConfidence(): this { + this._output = { ...(this._output || {}), includeConfidence: true }; + return this; + } + + // --- Reasoning Methods --- + + reasoning(settings: ChatReasoning): this { + this._reasoning = { ...(this._reasoning || {}), ...settings }; + return this; + } + + reasoningStyle(style: ReasoningStyle): this { + this._reasoning = { ...(this._reasoning || {}), style }; + return this; + } + + stepByStep(): this { + this._reasoning = { ...(this._reasoning || {}), style: 'step-by-step', showWork: true }; + return this; + } + + chainOfThought(): this { + this._reasoning = { ...(this._reasoning || {}), style: 'chain-of-thought', showWork: true }; + return this; + } + + treeOfThought(): this { + this._reasoning = { ...(this._reasoning || {}), style: 'tree-of-thought', showWork: true }; + return this; + } + + firstPrinciples(): this { + this._reasoning = { ...(this._reasoning || {}), style: 'first-principles', showWork: true }; + return this; + } + + devilsAdvocate(): this { + this._reasoning = { ...(this._reasoning || {}), style: 'devil-advocate', considerAlternatives: true }; + return this; + } + + showWork(show = true): this { + this._reasoning = { ...(this._reasoning || {}), showWork: show }; + return this; + } + + verifyAnswer(verify = true): this { + this._reasoning = { ...(this._reasoning || {}), verifyAnswer: verify }; + return this; + } + + considerAlternatives(consider = true): this { + this._reasoning = { ...(this._reasoning || {}), considerAlternatives: consider }; + return this; + } + + explainAssumptions(explain = true): this { + this._reasoning = { ...(this._reasoning || {}), explainAssumptions: explain }; + return this; + } + + // --- Memory Methods --- + + memory(memory: ChatMemory): this { + this._memory = { ...(this._memory || {}), ...memory }; + return this; + } + + remember(facts: string[]): this { + const existing = this._memory?.facts || []; + this._memory = { ...(this._memory || {}), facts: [...existing, ...facts] }; + return this; + } + + preferences(prefs: string[]): this { + this._memory = { ...(this._memory || {}), preferences: prefs }; + return this; + } + + history(messages: ChatMessage[]): this { + this._memory = { ...(this._memory || {}), history: messages }; + return this; + } + + summarizeHistory(summary: string): this { + this._memory = { ...(this._memory || {}), summary }; + return this; + } + + // --- Custom System Prompt Parts --- + + addSystemPart(part: string): this { + this._customSystemParts.push(part); + return this; + } + + raw(content: string): this { + this._customSystemParts = [content]; + return this; + } + + // --- Build Methods --- + + private buildSystemPrompt(): string { + const parts: string[] = []; + + // Persona + if (this._persona) { + let personaText = ''; + if (this._persona.name) { + personaText += `You are ${this._persona.name}`; + if (this._persona.role) personaText += `, ${this._persona.role}`; + personaText += '.'; + } else if (this._persona.role) { + personaText += `You are ${this._persona.role}.`; + } + + if (this._persona.tone) { + const tones = Array.isArray(this._persona.tone) ? this._persona.tone : [this._persona.tone]; + personaText += ` Your tone is ${tones.join(' and ')}.`; + } + + if (this._persona.expertise) { + const areas = Array.isArray(this._persona.expertise) ? this._persona.expertise : [this._persona.expertise]; + personaText += ` You have expertise in ${areas.join(', ')}.`; + } + + if (this._persona.personality?.length) { + personaText += ` You are ${this._persona.personality.join(', ')}.`; + } + + if (this._persona.background) { + personaText += ` ${this._persona.background}`; + } + + if (this._persona.verbosity) { + personaText += ` Keep responses ${this._persona.verbosity}.`; + } + + if (this._persona.language) { + personaText += ` Respond in ${this._persona.language}.`; + } + + if (personaText) parts.push(personaText.trim()); + } + + // Context + if (this._context) { + const contextParts: string[] = []; + + if (this._context.background) { + contextParts.push(this._context.background); + } + if (this._context.domain) { + contextParts.push(`Domain: ${this._context.domain}`); + } + if (this._context.audience) { + contextParts.push(`Target audience: ${this._context.audience}`); + } + if (this._context.purpose) { + contextParts.push(`Purpose: ${this._context.purpose}`); + } + if (this._context.knowledge?.length) { + contextParts.push(`Known facts:\n${this._context.knowledge.map(k => `- ${k}`).join('\n')}`); + } + if (this._context.assumptions?.length) { + contextParts.push(`Assumptions:\n${this._context.assumptions.map(a => `- ${a}`).join('\n')}`); + } + + if (contextParts.length) { + parts.push(`## Context\n${contextParts.join('\n')}`); + } + } + + // Task + if (this._task) { + const taskParts: string[] = []; + + if (this._task.instruction) { + taskParts.push(this._task.instruction); + } + if (this._task.priority) { + taskParts.push(`Priority: ${this._task.priority}`); + } + if (this._task.steps?.length) { + taskParts.push(`\nSteps:\n${this._task.steps.map((s, i) => `${i + 1}. ${s}`).join('\n')}`); + } + if (this._task.deliverables?.length) { + taskParts.push(`\nDeliverables:\n${this._task.deliverables.map(d => `- ${d}`).join('\n')}`); + } + if (this._task.criteria?.length) { + taskParts.push(`\nSuccess criteria:\n${this._task.criteria.map(c => `- ${c}`).join('\n')}`); + } + if (this._task.antiPatterns?.length) { + taskParts.push(`\nAvoid:\n${this._task.antiPatterns.map(a => `- ${a}`).join('\n')}`); + } + + if (taskParts.length) { + parts.push(`## Task\n${taskParts.join('\n')}`); + } + } + + // Constraints + if (this._context?.constraints?.length) { + parts.push(`## Constraints\n${this._context.constraints.map((c, i) => `${i + 1}. ${c}`).join('\n')}`); + } + + // Examples + if (this._examples.length) { + const examplesText = this._examples + .map((ex, i) => { + let text = `### Example ${i + 1}\n**Input:** ${ex.input}\n**Output:** ${ex.output}`; + if (ex.explanation) text += `\n**Explanation:** ${ex.explanation}`; + return text; + }) + .join('\n\n'); + parts.push(`## Examples\n${examplesText}`); + } + + // Output format + if (this._output) { + const outputParts: string[] = []; + + if (this._output.format) { + switch (this._output.format.type) { + case 'json': + if (this._output.format.jsonSchema) { + outputParts.push(`Respond in valid JSON matching this schema:\n\`\`\`json\n${JSON.stringify(this._output.format.jsonSchema.schema, null, 2)}\n\`\`\``); + } else { + outputParts.push('Respond in valid JSON format.'); + } + break; + case 'markdown': + outputParts.push('Format your response using Markdown.'); + break; + case 'code': + outputParts.push(`Respond with code${this._output.format.language ? ` in ${this._output.format.language}` : ''}.`); + break; + case 'table': + outputParts.push('Format your response as a table.'); + break; + } + } + if (this._output.length) { + outputParts.push(`Keep your response ${this._output.length}.`); + } + if (this._output.style) { + const styleMap: Record = { + 'prose': 'flowing prose', + 'bullet-points': 'bullet points', + 'numbered-list': 'a numbered list', + 'table': 'a table', + 'code': 'code', + 'mixed': 'a mix of prose and lists', + 'qa': 'Q&A format', + 'dialogue': 'dialogue format', + }; + outputParts.push(`Structure as ${styleMap[this._output.style]}.`); + } + if (this._output.language) { + outputParts.push(`Respond in ${this._output.language}.`); + } + if (this._output.includeExamples) { + outputParts.push('Include relevant examples.'); + } + if (this._output.includeExplanation) { + outputParts.push('Include clear explanations.'); + } + if (this._output.includeSources) { + outputParts.push('Cite your sources.'); + } + if (this._output.includeConfidence) { + outputParts.push('Include your confidence level in the answer.'); + } + + if (outputParts.length) { + parts.push(`## Output Format\n${outputParts.join('\n')}`); + } + } + + // Reasoning + if (this._reasoning) { + const reasoningParts: string[] = []; + + if (this._reasoning.style) { + const styleInstructions: Record = { + 'step-by-step': 'Think through this step by step.', + 'chain-of-thought': 'Use chain-of-thought reasoning to work through the problem.', + 'tree-of-thought': 'Consider multiple approaches and evaluate each before deciding.', + 'direct': 'Provide a direct answer.', + 'analytical': 'Analyze the problem systematically.', + 'comparative': 'Compare different options or approaches.', + 'deductive': 'Use deductive reasoning from general principles.', + 'inductive': 'Use inductive reasoning from specific examples.', + 'first-principles': 'Reason from first principles, breaking down to fundamental truths.', + 'analogical': 'Use analogies to explain and reason about the problem.', + 'devil-advocate': 'Consider and argue against your own conclusions.', + }; + reasoningParts.push(styleInstructions[this._reasoning.style]); + } + if (this._reasoning.showWork) { + reasoningParts.push('Show your reasoning process.'); + } + if (this._reasoning.verifyAnswer) { + reasoningParts.push('Verify your answer before presenting it.'); + } + if (this._reasoning.considerAlternatives) { + reasoningParts.push('Consider alternative perspectives and solutions.'); + } + if (this._reasoning.explainAssumptions) { + reasoningParts.push('Explicitly state any assumptions you make.'); + } + + if (reasoningParts.length) { + parts.push(`## Reasoning\n${reasoningParts.join('\n')}`); + } + } + + // Memory + if (this._memory) { + const memoryParts: string[] = []; + + if (this._memory.summary) { + memoryParts.push(`Previous conversation summary: ${this._memory.summary}`); + } + if (this._memory.facts?.length) { + memoryParts.push(`Known facts:\n${this._memory.facts.map(f => `- ${f}`).join('\n')}`); + } + if (this._memory.preferences?.length) { + memoryParts.push(`User preferences:\n${this._memory.preferences.map(p => `- ${p}`).join('\n')}`); + } + + if (memoryParts.length) { + parts.push(`## Memory\n${memoryParts.join('\n')}`); + } + } + + // Custom parts + if (this._customSystemParts.length) { + parts.push(...this._customSystemParts); + } + + return parts.join('\n\n'); + } + + build(): BuiltChatPrompt { + const systemPrompt = this.buildSystemPrompt(); + + // Ensure system message is first + let messages = [...this._messages]; + const hasSystemMessage = messages.some(m => m.role === 'system'); + + if (systemPrompt && !hasSystemMessage) { + messages = [{ role: 'system', content: systemPrompt }, ...messages]; + } else if (systemPrompt && hasSystemMessage) { + // Prepend built system prompt to existing system message + messages = messages.map(m => + m.role === 'system' ? { ...m, content: `${systemPrompt}\n\n${m.content}` } : m + ); + } + + // Add memory history if present + if (this._memory?.history) { + const systemIdx = messages.findIndex(m => m.role === 'system'); + const insertIdx = systemIdx >= 0 ? systemIdx + 1 : 0; + messages.splice(insertIdx, 0, ...this._memory.history); + } + + // Get user prompt if exists + const userMessages = messages.filter(m => m.role === 'user'); + const userPrompt = userMessages.length ? userMessages[userMessages.length - 1].content : undefined; + + return { + messages, + systemPrompt, + userPrompt, + metadata: { + persona: this._persona, + context: this._context, + task: this._task, + output: this._output, + reasoning: this._reasoning, + examples: this._examples.length ? this._examples : undefined, + }, + }; + } + + // --- Output Methods --- + + toString(): string { + return this.buildSystemPrompt(); + } + + toSystemPrompt(): string { + return this.buildSystemPrompt(); + } + + toMessages(): ChatMessage[] { + return this.build().messages; + } + + toJSON(): string { + return JSON.stringify(this.build(), null, 2); + } + + toYAML(): string { + return objectToYaml(this.build()); + } + + toMarkdown(): string { + const built = this.build(); + const sections: string[] = ['# Chat Prompt\n']; + + sections.push('## System Prompt\n```\n' + built.systemPrompt + '\n```\n'); + + if (built.messages.length > 1) { + sections.push('## Messages\n'); + for (const msg of built.messages) { + if (msg.role === 'system') continue; + sections.push(`**${msg.role.toUpperCase()}${msg.name ? ` (${msg.name})` : ''}:**\n${msg.content}\n`); + } + } + + return sections.join('\n'); + } +} + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +function objectToYaml(obj: object, indent = 0): string { + const spaces = ' '.repeat(indent); + const lines: string[] = []; + + for (const [key, value] of Object.entries(obj)) { + if (value === undefined || value === null) continue; + + if (Array.isArray(value)) { + if (value.length === 0) continue; + lines.push(`${spaces}${key}:`); + for (const item of value) { + if (typeof item === 'object') { + lines.push(`${spaces} -`); + lines.push(objectToYaml(item, indent + 2).replace(/^/gm, ' ')); + } else { + lines.push(`${spaces} - ${item}`); + } + } + } else if (typeof value === 'object') { + lines.push(`${spaces}${key}:`); + lines.push(objectToYaml(value, indent + 1)); + } else if (typeof value === 'string' && value.includes('\n')) { + lines.push(`${spaces}${key}: |`); + for (const line of value.split('\n')) { + lines.push(`${spaces} ${line}`); + } + } else { + lines.push(`${spaces}${key}: ${value}`); + } + } + + return lines.join('\n'); +} + +// ============================================================================ +// FACTORY FUNCTION +// ============================================================================ + +/** + * Create a new chat prompt builder + */ +export function chat(): ChatPromptBuilder { + return new ChatPromptBuilder(); +} + +// ============================================================================ +// PRESET BUILDERS +// ============================================================================ + +export const chatPresets = { + /** + * Code assistant preset + */ + coder: (language?: string) => { + const c = chat() + .role("expert software developer") + .expertise("coding") + .tone("technical"); + + if (language) { + c.context(`Programming language: ${language}`); + } + + return c; + }, + + /** + * Writing assistant preset + */ + writer: (style?: 'creative' | 'professional' | 'academic') => { + const c = chat() + .role("skilled writer and editor") + .expertise("writing"); + + if (style) { + c.tone(style === 'creative' ? 'creative' : style === 'academic' ? 'academic' : 'professional'); + } + + return c; + }, + + /** + * Teacher/tutor preset + */ + tutor: (subject?: string) => { + const c = chat() + .role("patient and knowledgeable tutor") + .expertise("teaching") + .tone(['friendly', 'empathetic']) + .stepByStep() + .withExamples(); + + if (subject) { + c.domain(subject); + } + + return c; + }, + + /** + * Analyst preset + */ + analyst: () => { + return chat() + .role("data analyst and researcher") + .expertise("analysis") + .tone("analytical") + .chainOfThought() + .detailed() + .withSources(); + }, + + /** + * Socratic dialogue preset + */ + socratic: () => { + return chat() + .role("Socratic philosopher and teacher") + .tone("socratic") + .reasoning({ style: 'deductive', showWork: true }) + .avoid(["Give direct answers", "Lecture", "Be condescending"]); + }, + + /** + * Critic preset + */ + critic: () => { + return chat() + .role("constructive critic") + .tone(['analytical', 'professional']) + .devilsAdvocate() + .detailed() + .avoid(["Be harsh", "Dismiss ideas without explanation"]); + }, + + /** + * Brainstormer preset + */ + brainstormer: () => { + return chat() + .role("creative brainstorming partner") + .tone(['creative', 'encouraging']) + .expertise("creative") + .considerAlternatives() + .avoid(["Dismiss ideas", "Be negative", "Limit creativity"]); + }, + + /** + * JSON response preset + */ + jsonResponder: (schemaName: string, schema: Record) => { + return chat() + .role("data processing assistant") + .tone("concise") + .jsonSchema(schemaName, schema) + .avoid(["Include markdown", "Add explanations outside JSON", "Include code fences"]); + }, + + /** + * Summarizer preset + */ + summarizer: (length: OutputLength = 'brief') => { + return chat() + .role("expert summarizer") + .expertise("analysis") + .tone("concise") + .length(length) + .task("Summarize the provided content, preserving key information"); + }, + + /** + * Translator preset + */ + translator: (targetLanguage: string) => { + return chat() + .role("professional translator") + .expertise("languages") + .responseLanguage(targetLanguage) + .avoid(["Add commentary", "Change meaning", "Omit content"]); + }, +}; diff --git a/packages/prompts.chat/src/builder/index.ts b/packages/prompts.chat/src/builder/index.ts new file mode 100644 index 00000000..6c072f0d --- /dev/null +++ b/packages/prompts.chat/src/builder/index.ts @@ -0,0 +1,449 @@ +/** + * Prompt Builder - A fluent DSL for creating structured prompts + * + * @example + * ```ts + * import { builder } from 'prompts.chat'; + * + * const prompt = builder() + * .role("Senior TypeScript Developer") + * .context("You are helping review code") + * .task("Analyze the following code for bugs") + * .constraints(["Be concise", "Focus on critical issues"]) + * .output("JSON with { bugs: [], suggestions: [] }") + * .variable("code", { required: true }) + * .build(); + * ``` + */ + +export interface PromptVariable { + name: string; + description?: string; + required?: boolean; + defaultValue?: string; +} + +export interface BuiltPrompt { + content: string; + variables: PromptVariable[]; + metadata: { + role?: string; + context?: string; + task?: string; + constraints?: string[]; + outputFormat?: string; + examples?: Array<{ input: string; output: string }>; + }; +} + +export class PromptBuilder { + private _role?: string; + private _context?: string; + private _task?: string; + private _constraints: string[] = []; + private _outputFormat?: string; + private _examples: Array<{ input: string; output: string }> = []; + private _variables: PromptVariable[] = []; + private _customSections: Array<{ title: string; content: string }> = []; + private _rawContent?: string; + + /** + * Set the role/persona for the AI + */ + role(role: string): this { + this._role = role; + return this; + } + + /** + * Alias for role() + */ + persona(persona: string): this { + return this.role(persona); + } + + /** + * Set the context/background information + */ + context(context: string): this { + this._context = context; + return this; + } + + /** + * Alias for context() + */ + background(background: string): this { + return this.context(background); + } + + /** + * Set the main task/instruction + */ + task(task: string): this { + this._task = task; + return this; + } + + /** + * Alias for task() + */ + instruction(instruction: string): this { + return this.task(instruction); + } + + /** + * Add constraints/rules the AI should follow + */ + constraints(constraints: string[]): this { + this._constraints = [...this._constraints, ...constraints]; + return this; + } + + /** + * Add a single constraint + */ + constraint(constraint: string): this { + this._constraints.push(constraint); + return this; + } + + /** + * Alias for constraints() + */ + rules(rules: string[]): this { + return this.constraints(rules); + } + + /** + * Set the expected output format + */ + output(format: string): this { + this._outputFormat = format; + return this; + } + + /** + * Alias for output() + */ + format(format: string): this { + return this.output(format); + } + + /** + * Add an example input/output pair + */ + example(input: string, output: string): this { + this._examples.push({ input, output }); + return this; + } + + /** + * Add multiple examples + */ + examples(examples: Array<{ input: string; output: string }>): this { + this._examples = [...this._examples, ...examples]; + return this; + } + + /** + * Define a variable placeholder + */ + variable( + name: string, + options: { description?: string; required?: boolean; defaultValue?: string } = {} + ): this { + this._variables.push({ + name, + description: options.description, + required: options.required ?? true, + defaultValue: options.defaultValue, + }); + return this; + } + + /** + * Add a custom section + */ + section(title: string, content: string): this { + this._customSections.push({ title, content }); + return this; + } + + /** + * Set raw content (bypasses structured building) + */ + raw(content: string): this { + this._rawContent = content; + return this; + } + + /** + * Build the final prompt + */ + build(): BuiltPrompt { + if (this._rawContent) { + return { + content: this._rawContent, + variables: this._variables, + metadata: {}, + }; + } + + const sections: string[] = []; + + // Role section + if (this._role) { + sections.push(`You are ${this._role}.`); + } + + // Context section + if (this._context) { + sections.push(`\n## Context\n${this._context}`); + } + + // Task section + if (this._task) { + sections.push(`\n## Task\n${this._task}`); + } + + // Constraints section + if (this._constraints.length > 0) { + const constraintsList = this._constraints + .map((c, i) => `${i + 1}. ${c}`) + .join('\n'); + sections.push(`\n## Constraints\n${constraintsList}`); + } + + // Output format section + if (this._outputFormat) { + sections.push(`\n## Output Format\n${this._outputFormat}`); + } + + // Examples section + if (this._examples.length > 0) { + const examplesText = this._examples + .map((e, i) => `### Example ${i + 1}\n**Input:** ${e.input}\n**Output:** ${e.output}`) + .join('\n\n'); + sections.push(`\n## Examples\n${examplesText}`); + } + + // Custom sections + for (const section of this._customSections) { + sections.push(`\n## ${section.title}\n${section.content}`); + } + + // Variables section (as placeholders info) + if (this._variables.length > 0) { + const varsText = this._variables + .map(v => { + const placeholder = v.defaultValue + ? `\${${v.name}:${v.defaultValue}}` + : `\${${v.name}}`; + const desc = v.description ? ` - ${v.description}` : ''; + const req = v.required ? ' (required)' : ' (optional)'; + return `- ${placeholder}${desc}${req}`; + }) + .join('\n'); + sections.push(`\n## Variables\n${varsText}`); + } + + return { + content: sections.join('\n').trim(), + variables: this._variables, + metadata: { + role: this._role, + context: this._context, + task: this._task, + constraints: this._constraints.length > 0 ? this._constraints : undefined, + outputFormat: this._outputFormat, + examples: this._examples.length > 0 ? this._examples : undefined, + }, + }; + } + + /** + * Build and return only the content string + */ + toString(): string { + return this.build().content; + } +} + +/** + * Create a new prompt builder + */ +export function builder(): PromptBuilder { + return new PromptBuilder(); +} + +/** + * Create a prompt builder from an existing prompt + */ +export function fromPrompt(content: string): PromptBuilder { + return new PromptBuilder().raw(content); +} + +// Re-export media builders +export { image, ImagePromptBuilder } from './media'; +export type { + BuiltImagePrompt, + ImageSubject, + ImageCamera, + ImageLighting, + ImageComposition, + ImageStyle, + ImageColor, + ImageEnvironment, + ImageTechnical, + CameraAngle, + ShotType, + LensType, + LightingType, + TimeOfDay, + ArtStyle, + ColorPalette, + Mood, + AspectRatio, + OutputFormat, +} from './media'; + +export { video, VideoPromptBuilder } from './video'; +export type { + BuiltVideoPrompt, + VideoScene, + VideoSubject, + VideoCamera, + VideoLighting, + VideoAction, + VideoMotion, + VideoStyle, + VideoColor, + VideoAudio, + VideoTechnical, + VideoShot, +} from './video'; + +export { audio, AudioPromptBuilder } from './audio'; +export type { + BuiltAudioPrompt, + AudioGenre, + AudioMood, + AudioTempo, + AudioVocals, + AudioInstrumentation, + AudioStructure, + AudioProduction, + AudioTechnical, + MusicGenre, + Instrument, + VocalStyle, + TempoMarking, + TimeSignature, + MusicalKey, + SongSection, + ProductionStyle, +} from './audio'; + +// Re-export chat builder +export { chat, ChatPromptBuilder, chatPresets } from './chat'; +export type { + BuiltChatPrompt, + ChatMessage, + ChatPersona, + ChatContext, + ChatTask, + ChatOutput, + ChatReasoning, + ChatMemory, + ChatExample, + MessageRole, + ResponseFormat, + ResponseFormatType, + JsonSchema, + PersonaTone, + PersonaExpertise, + ReasoningStyle, + OutputLength, + OutputStyle, +} from './chat'; + +// Pre-built templates +export const templates = { + /** + * Create a code review prompt + */ + codeReview: (options: { language?: string; focus?: string[] } = {}) => { + const b = builder() + .role("expert code reviewer") + .task("Review the provided code and identify issues, improvements, and best practices.") + .variable("code", { required: true, description: "The code to review" }); + + if (options.language) { + b.context(`You are reviewing ${options.language} code.`); + } + + if (options.focus && options.focus.length > 0) { + b.constraints(options.focus.map(f => `Focus on ${f}`)); + } + + return b.output("Provide a structured review with: issues found, suggestions, and overall assessment."); + }, + + /** + * Create a translation prompt + */ + translation: (from: string, to: string) => { + return builder() + .role(`professional translator fluent in ${from} and ${to}`) + .task(`Translate the following text from ${from} to ${to}.`) + .constraints([ + "Maintain the original meaning and tone", + "Use natural, idiomatic expressions in the target language", + "Preserve formatting and structure", + ]) + .variable("text", { required: true, description: "Text to translate" }); + }, + + /** + * Create a summarization prompt + */ + summarize: (options: { maxLength?: number; style?: 'bullet' | 'paragraph' } = {}) => { + const b = builder() + .role("expert summarizer") + .task("Summarize the following content concisely while preserving key information.") + .variable("content", { required: true, description: "Content to summarize" }); + + if (options.maxLength) { + b.constraint(`Keep the summary under ${options.maxLength} words`); + } + + if (options.style === 'bullet') { + b.output("Provide the summary as bullet points"); + } + + return b; + }, + + /** + * Create a Q&A prompt + */ + qa: (context?: string) => { + const b = builder() + .role("helpful assistant") + .task("Answer the question based on the provided context.") + .variable("question", { required: true, description: "The question to answer" }); + + if (context) { + b.context(context); + } else { + b.variable("context", { required: false, description: "Additional context" }); + } + + return b.constraints([ + "Be accurate and concise", + "If you don't know the answer, say so", + "Cite relevant parts of the context if applicable", + ]); + }, +}; diff --git a/packages/prompts.chat/src/builder/media.ts b/packages/prompts.chat/src/builder/media.ts new file mode 100644 index 00000000..6ccfbb5a --- /dev/null +++ b/packages/prompts.chat/src/builder/media.ts @@ -0,0 +1,982 @@ +/** + * Media Prompt Builders - The D3.js of Prompt Building + * + * Comprehensive, structured builders for Image, Video, and Audio generation prompts. + * Every attribute a professional would consider is available as a chainable method. + * + * @example + * ```ts + * import { image, video, audio } from 'prompts.chat/builder'; + * + * const imagePrompt = image() + * .subject("a lone samurai") + * .environment("bamboo forest at dawn") + * .camera({ angle: "low", shot: "wide", lens: "35mm" }) + * .lighting({ type: "rim", time: "golden-hour" }) + * .style({ artist: "Akira Kurosawa", medium: "cinematic" }) + * .build(); + * ``` + */ + +// ============================================================================ +// TYPES & INTERFACES +// ============================================================================ + +export type OutputFormat = 'text' | 'json' | 'yaml' | 'markdown'; + +// --- Camera Brands & Models --- +export type CameraBrand = + | 'sony' | 'canon' | 'nikon' | 'fujifilm' | 'leica' | 'hasselblad' | 'phase-one' + | 'panasonic' | 'olympus' | 'pentax' | 'red' | 'arri' | 'blackmagic' | 'panavision'; + +export type CameraModel = + // Sony + | 'sony-a7iv' | 'sony-a7riv' | 'sony-a7siii' | 'sony-a1' | 'sony-fx3' | 'sony-fx6' + | 'sony-venice' | 'sony-venice-2' | 'sony-a9ii' | 'sony-zv-e1' + // Canon + | 'canon-r5' | 'canon-r6' | 'canon-r3' | 'canon-r8' | 'canon-c70' | 'canon-c300-iii' + | 'canon-c500-ii' | 'canon-5d-iv' | 'canon-1dx-iii' | 'canon-eos-r5c' + // Nikon + | 'nikon-z9' | 'nikon-z8' | 'nikon-z6-iii' | 'nikon-z7-ii' | 'nikon-d850' | 'nikon-d6' + // Fujifilm + | 'fujifilm-x-t5' | 'fujifilm-x-h2s' | 'fujifilm-x100vi' | 'fujifilm-gfx100s' + | 'fujifilm-gfx100-ii' | 'fujifilm-x-pro3' + // Leica + | 'leica-m11' | 'leica-sl2' | 'leica-sl2-s' | 'leica-q3' | 'leica-m10-r' + // Hasselblad + | 'hasselblad-x2d' | 'hasselblad-907x' | 'hasselblad-h6d-100c' + // Cinema Cameras + | 'arri-alexa-35' | 'arri-alexa-mini-lf' | 'arri-alexa-65' | 'arri-amira' + | 'red-v-raptor' | 'red-komodo' | 'red-gemini' | 'red-monstro' + | 'blackmagic-ursa-mini-pro' | 'blackmagic-pocket-6k' | 'blackmagic-pocket-4k' + | 'panavision-dxl2' | 'panavision-millennium-xl2'; + +export type SensorFormat = + | 'full-frame' | 'aps-c' | 'micro-four-thirds' | 'medium-format' | 'large-format' + | 'super-35' | 'vista-vision' | 'imax' | '65mm' | '35mm-film' | '16mm-film' | '8mm-film'; + +export type FilmFormat = + | '35mm' | '120-medium-format' | '4x5-large-format' | '8x10-large-format' + | '110-film' | 'instant-film' | 'super-8' | '16mm' | '65mm-imax'; + +// --- Camera & Shot Types --- +export type CameraAngle = + | 'eye-level' | 'low-angle' | 'high-angle' | 'dutch-angle' | 'birds-eye' + | 'worms-eye' | 'over-the-shoulder' | 'point-of-view' | 'aerial' | 'drone' + | 'canted' | 'oblique' | 'hip-level' | 'knee-level' | 'ground-level'; + +export type ShotType = + | 'extreme-close-up' | 'close-up' | 'medium-close-up' | 'medium' | 'medium-wide' + | 'wide' | 'extreme-wide' | 'establishing' | 'full-body' | 'portrait' | 'headshot'; + +export type LensType = + | 'wide-angle' | 'ultra-wide' | 'standard' | 'telephoto' | 'macro' | 'fisheye' + | '14mm' | '24mm' | '35mm' | '50mm' | '85mm' | '100mm' | '135mm' | '200mm' | '400mm' + | '600mm' | '800mm' | 'tilt-shift' | 'anamorphic' | 'spherical' | 'prime' | 'zoom'; + +export type LensBrand = + | 'zeiss' | 'leica' | 'canon' | 'nikon' | 'sony' | 'sigma' | 'tamron' | 'voigtlander' + | 'fujifilm' | 'samyang' | 'rokinon' | 'tokina' | 'cooke' | 'arri' | 'panavision' + | 'angenieux' | 'red' | 'atlas' | 'sirui'; + +export type LensModel = + // Zeiss + | 'zeiss-otus-55' | 'zeiss-batis-85' | 'zeiss-milvus-35' | 'zeiss-supreme-prime' + // Leica + | 'leica-summilux-50' | 'leica-summicron-35' | 'leica-noctilux-50' | 'leica-apo-summicron' + // Canon + | 'canon-rf-50-1.2' | 'canon-rf-85-1.2' | 'canon-rf-28-70-f2' | 'canon-rf-100-500' + // Sony + | 'sony-gm-24-70' | 'sony-gm-70-200' | 'sony-gm-50-1.2' | 'sony-gm-85-1.4' + // Sigma Art + | 'sigma-art-35' | 'sigma-art-50' | 'sigma-art-85' | 'sigma-art-105-macro' + // Cinema + | 'cooke-s7i' | 'cooke-anamorphic' | 'arri-signature-prime' | 'arri-ultra-prime' + | 'panavision-primo' | 'panavision-anamorphic' | 'atlas-orion-anamorphic' + // Vintage + | 'helios-44-2' | 'canon-fd-55' | 'minolta-rokkor-58' | 'pentax-takumar-50'; + +export type FocusType = + | 'shallow' | 'deep' | 'soft-focus' | 'tilt-shift' | 'rack-focus' | 'split-diopter' + | 'zone-focus' | 'hyperfocal' | 'selective' | 'bokeh-heavy' | 'tack-sharp'; + +export type BokehStyle = + | 'smooth' | 'creamy' | 'swirly' | 'busy' | 'soap-bubble' | 'cat-eye' | 'oval-anamorphic'; + +export type FilterType = + | 'uv' | 'polarizer' | 'nd' | 'nd-graduated' | 'black-pro-mist' | 'white-pro-mist' + | 'glimmer-glass' | 'classic-soft' | 'streak' | 'starburst' | 'diffusion' + | 'infrared' | 'color-gel' | 'warming' | 'cooling' | 'vintage-look'; + +export type CameraMovement = + | 'static' | 'pan' | 'tilt' | 'dolly' | 'truck' | 'pedestal' | 'zoom' + | 'handheld' | 'steadicam' | 'crane' | 'drone' | 'tracking' | 'arc' | 'whip-pan' + | 'roll' | 'boom' | 'jib' | 'cable-cam' | 'motion-control' | 'snorricam' + | 'dutch-roll' | 'vertigo-effect' | 'crash-zoom' | 'slow-push' | 'slow-pull'; + +export type CameraRig = + | 'tripod' | 'monopod' | 'gimbal' | 'steadicam' | 'easyrig' | 'shoulder-rig' + | 'slider' | 'dolly' | 'jib' | 'crane' | 'technocrane' | 'russian-arm' + | 'cable-cam' | 'drone' | 'fpv-drone' | 'motion-control' | 'handheld'; + +export type GimbalModel = + | 'dji-ronin-4d' | 'dji-ronin-rs3-pro' | 'dji-ronin-rs4' | 'moza-air-2' + | 'zhiyun-crane-3s' | 'freefly-movi-pro' | 'tilta-gravity-g2x'; + +// --- Lighting Types --- +export type LightingType = + | 'natural' | 'studio' | 'dramatic' | 'soft' | 'hard' | 'diffused' + | 'key' | 'fill' | 'rim' | 'backlit' | 'silhouette' | 'rembrandt' + | 'split' | 'butterfly' | 'loop' | 'broad' | 'short' | 'chiaroscuro' + | 'high-key' | 'low-key' | 'three-point' | 'practical' | 'motivated'; + +export type TimeOfDay = + | 'dawn' | 'sunrise' | 'golden-hour' | 'morning' | 'midday' | 'afternoon' + | 'blue-hour' | 'sunset' | 'dusk' | 'twilight' | 'night' | 'midnight'; + +export type WeatherLighting = + | 'sunny' | 'cloudy' | 'overcast' | 'foggy' | 'misty' | 'rainy' + | 'stormy' | 'snowy' | 'hazy'; + +// --- Style Types --- +export type ArtStyle = + | 'photorealistic' | 'hyperrealistic' | 'cinematic' | 'documentary' + | 'editorial' | 'fashion' | 'portrait' | 'landscape' | 'street' + | 'fine-art' | 'conceptual' | 'surreal' | 'abstract' | 'minimalist' + | 'maximalist' | 'vintage' | 'retro' | 'noir' | 'gothic' | 'romantic' + | 'impressionist' | 'expressionist' | 'pop-art' | 'art-nouveau' | 'art-deco' + | 'cyberpunk' | 'steampunk' | 'fantasy' | 'sci-fi' | 'anime' | 'manga' + | 'comic-book' | 'illustration' | 'digital-art' | 'oil-painting' | 'watercolor' + | 'sketch' | 'pencil-drawing' | 'charcoal' | 'pastel' | '3d-render'; + +export type FilmStock = + // Kodak Color Negative + | 'kodak-portra-160' | 'kodak-portra-400' | 'kodak-portra-800' + | 'kodak-ektar-100' | 'kodak-gold-200' | 'kodak-ultramax-400' | 'kodak-colorplus-200' + // Kodak Black & White + | 'kodak-tri-x-400' | 'kodak-tmax-100' | 'kodak-tmax-400' | 'kodak-tmax-3200' + // Kodak Slide + | 'kodak-ektachrome-e100' | 'kodachrome-64' | 'kodachrome-200' + // Kodak Cinema + | 'kodak-vision3-50d' | 'kodak-vision3-200t' | 'kodak-vision3-250d' | 'kodak-vision3-500t' + // Fujifilm + | 'fujifilm-pro-400h' | 'fujifilm-superia-400' | 'fujifilm-c200' + | 'fujifilm-velvia-50' | 'fujifilm-velvia-100' | 'fujifilm-provia-100f' + | 'fujifilm-acros-100' | 'fujifilm-neopan-400' | 'fujifilm-eterna-500t' + // Ilford + | 'ilford-hp5-plus' | 'ilford-delta-100' | 'ilford-delta-400' | 'ilford-delta-3200' + | 'ilford-fp4-plus' | 'ilford-pan-f-plus' | 'ilford-xp2-super' + // CineStill + | 'cinestill-50d' | 'cinestill-800t' | 'cinestill-400d' | 'cinestill-bwxx' + // Lomography + | 'lomography-100' | 'lomography-400' | 'lomography-800' + | 'lomochrome-purple' | 'lomochrome-metropolis' | 'lomochrome-turquoise' + // Instant + | 'polaroid-sx-70' | 'polaroid-600' | 'polaroid-i-type' | 'polaroid-spectra' + | 'instax-mini' | 'instax-wide' | 'instax-square' + // Vintage/Discontinued + | 'agfa-vista-400' | 'agfa-apx-100' | 'fomapan-100' | 'fomapan-400' + | 'bergger-pancro-400' | 'jch-streetpan-400'; + +export type AspectRatio = + | '1:1' | '4:3' | '3:2' | '16:9' | '21:9' | '9:16' | '2:3' | '4:5' | '5:4'; + +// --- Color & Mood --- +export type ColorPalette = + | 'warm' | 'cool' | 'neutral' | 'vibrant' | 'muted' | 'pastel' | 'neon' + | 'monochrome' | 'sepia' | 'desaturated' | 'high-contrast' | 'low-contrast' + | 'earthy' | 'oceanic' | 'forest' | 'sunset' | 'midnight' | 'golden'; + +export type Mood = + | 'serene' | 'peaceful' | 'melancholic' | 'dramatic' | 'tense' | 'mysterious' + | 'romantic' | 'nostalgic' | 'hopeful' | 'joyful' | 'energetic' | 'chaotic' + | 'ethereal' | 'dark' | 'light' | 'whimsical' | 'eerie' | 'epic' | 'intimate'; + +// --- Video Specific --- +export type VideoTransition = + | 'cut' | 'fade' | 'dissolve' | 'wipe' | 'morph' | 'match-cut' | 'jump-cut' + | 'cross-dissolve' | 'iris' | 'push' | 'slide'; + +export type VideoPacing = + | 'slow' | 'medium' | 'fast' | 'variable' | 'building' | 'frenetic' | 'contemplative'; + +// --- Audio/Music Specific --- +export type MusicGenre = + | 'pop' | 'rock' | 'jazz' | 'classical' | 'electronic' | 'hip-hop' | 'r&b' + | 'country' | 'folk' | 'blues' | 'metal' | 'punk' | 'indie' | 'alternative' + | 'ambient' | 'lo-fi' | 'synthwave' | 'orchestral' | 'cinematic' | 'world' + | 'latin' | 'reggae' | 'soul' | 'funk' | 'disco' | 'house' | 'techno' | 'edm'; + +export type Instrument = + | 'piano' | 'guitar' | 'acoustic-guitar' | 'electric-guitar' | 'bass' | 'drums' + | 'violin' | 'cello' | 'viola' | 'flute' | 'saxophone' | 'trumpet' | 'trombone' + | 'synthesizer' | 'organ' | 'harp' | 'percussion' | 'strings' | 'brass' | 'woodwinds' + | 'choir' | 'vocals' | 'beatbox' | 'turntables' | 'harmonica' | 'banjo' | 'ukulele'; + +export type VocalStyle = + | 'male' | 'female' | 'duet' | 'choir' | 'a-cappella' | 'spoken-word' | 'rap' + | 'falsetto' | 'belting' | 'whisper' | 'growl' | 'melodic' | 'harmonized'; + +export type Tempo = + | 'largo' | 'adagio' | 'andante' | 'moderato' | 'allegro' | 'vivace' | 'presto' + | number; // BPM + +// ============================================================================ +// IMAGE PROMPT BUILDER +// ============================================================================ + +export interface ImageSubject { + main: string; + details?: string[]; + expression?: string; + pose?: string; + action?: string; + clothing?: string; + accessories?: string[]; + age?: string; + ethnicity?: string; + gender?: string; + count?: number | 'single' | 'couple' | 'group' | 'crowd'; +} + +export interface ImageCamera { + // Framing + angle?: CameraAngle; + shot?: ShotType; + + // Camera Body + brand?: CameraBrand; + model?: CameraModel; + sensor?: SensorFormat; + + // Lens + lens?: LensType; + lensModel?: LensModel; + lensBrand?: LensBrand; + focalLength?: string; + + // Focus & Depth + focus?: FocusType; + aperture?: string; + bokeh?: BokehStyle; + focusDistance?: string; + + // Exposure + iso?: number; + shutterSpeed?: string; + exposureCompensation?: string; + + // Film/Digital + filmStock?: FilmStock; + filmFormat?: FilmFormat; + + // Filters & Accessories + filter?: FilterType | FilterType[]; + + // Camera Settings + whiteBalance?: 'daylight' | 'cloudy' | 'tungsten' | 'fluorescent' | 'flash' | 'custom'; + colorProfile?: string; + pictureProfile?: string; +} + +export interface ImageLighting { + type?: LightingType | LightingType[]; + time?: TimeOfDay; + weather?: WeatherLighting; + direction?: 'front' | 'side' | 'back' | 'top' | 'bottom' | 'three-quarter'; + intensity?: 'soft' | 'medium' | 'hard' | 'dramatic'; + color?: string; + sources?: string[]; +} + +export interface ImageComposition { + ruleOfThirds?: boolean; + goldenRatio?: boolean; + symmetry?: 'none' | 'horizontal' | 'vertical' | 'radial'; + leadingLines?: boolean; + framing?: string; + negativeSpace?: boolean; + layers?: string[]; + foreground?: string; + midground?: string; + background?: string; +} + +export interface ImageStyle { + medium?: ArtStyle | ArtStyle[]; + artist?: string | string[]; + era?: string; + influence?: string[]; + quality?: string[]; + render?: string; +} + +export interface ImageColor { + palette?: ColorPalette | ColorPalette[]; + primary?: string[]; + accent?: string[]; + grade?: string; + temperature?: 'warm' | 'neutral' | 'cool'; + saturation?: 'desaturated' | 'natural' | 'vibrant' | 'hyper-saturated'; + contrast?: 'low' | 'medium' | 'high'; +} + +export interface ImageEnvironment { + setting: string; + location?: string; + terrain?: string; + architecture?: string; + props?: string[]; + atmosphere?: string; + season?: 'spring' | 'summer' | 'autumn' | 'winter'; + era?: string; +} + +export interface ImageTechnical { + aspectRatio?: AspectRatio; + resolution?: string; + quality?: 'draft' | 'standard' | 'high' | 'ultra' | 'masterpiece'; + detail?: 'low' | 'medium' | 'high' | 'extreme'; + noise?: 'none' | 'subtle' | 'filmic' | 'grainy'; + sharpness?: 'soft' | 'natural' | 'sharp' | 'crisp'; +} + +export interface BuiltImagePrompt { + prompt: string; + structure: { + subject?: ImageSubject; + camera?: ImageCamera; + lighting?: ImageLighting; + composition?: ImageComposition; + style?: ImageStyle; + color?: ImageColor; + environment?: ImageEnvironment; + technical?: ImageTechnical; + mood?: Mood | Mood[]; + negative?: string[]; + }; +} + +export class ImagePromptBuilder { + private _subject?: ImageSubject; + private _camera?: ImageCamera; + private _lighting?: ImageLighting; + private _composition?: ImageComposition; + private _style?: ImageStyle; + private _color?: ImageColor; + private _environment?: ImageEnvironment; + private _technical?: ImageTechnical; + private _mood?: Mood | Mood[]; + private _negative: string[] = []; + private _custom: string[] = []; + + // --- Subject Methods --- + + subject(main: string | ImageSubject): this { + if (typeof main === 'string') { + this._subject = { ...(this._subject || {}), main }; + } else { + this._subject = { ...(this._subject || {}), ...main }; + } + return this; + } + + subjectDetails(details: string[]): this { + this._subject = { ...(this._subject || { main: '' }), details }; + return this; + } + + expression(expression: string): this { + this._subject = { ...(this._subject || { main: '' }), expression }; + return this; + } + + pose(pose: string): this { + this._subject = { ...(this._subject || { main: '' }), pose }; + return this; + } + + action(action: string): this { + this._subject = { ...(this._subject || { main: '' }), action }; + return this; + } + + clothing(clothing: string): this { + this._subject = { ...(this._subject || { main: '' }), clothing }; + return this; + } + + accessories(accessories: string[]): this { + this._subject = { ...(this._subject || { main: '' }), accessories }; + return this; + } + + subjectCount(count: ImageSubject['count']): this { + this._subject = { ...(this._subject || { main: '' }), count }; + return this; + } + + // --- Camera Methods --- + + camera(settings: ImageCamera): this { + this._camera = { ...(this._camera || {}), ...settings }; + return this; + } + + angle(angle: CameraAngle): this { + this._camera = { ...(this._camera || {}), angle }; + return this; + } + + shot(shot: ShotType): this { + this._camera = { ...(this._camera || {}), shot }; + return this; + } + + lens(lens: LensType): this { + this._camera = { ...(this._camera || {}), lens }; + return this; + } + + focus(focus: FocusType): this { + this._camera = { ...(this._camera || {}), focus }; + return this; + } + + aperture(aperture: string): this { + this._camera = { ...(this._camera || {}), aperture }; + return this; + } + + filmStock(filmStock: FilmStock): this { + this._camera = { ...(this._camera || {}), filmStock }; + return this; + } + + filmFormat(format: FilmFormat): this { + this._camera = { ...(this._camera || {}), filmFormat: format }; + return this; + } + + cameraBrand(brand: CameraBrand): this { + this._camera = { ...(this._camera || {}), brand }; + return this; + } + + cameraModel(model: CameraModel): this { + this._camera = { ...(this._camera || {}), model }; + return this; + } + + sensor(sensor: SensorFormat): this { + this._camera = { ...(this._camera || {}), sensor }; + return this; + } + + lensModel(model: LensModel): this { + this._camera = { ...(this._camera || {}), lensModel: model }; + return this; + } + + lensBrand(brand: LensBrand): this { + this._camera = { ...(this._camera || {}), lensBrand: brand }; + return this; + } + + focalLength(length: string): this { + this._camera = { ...(this._camera || {}), focalLength: length }; + return this; + } + + bokeh(style: BokehStyle): this { + this._camera = { ...(this._camera || {}), bokeh: style }; + return this; + } + + filter(filter: FilterType | FilterType[]): this { + this._camera = { ...(this._camera || {}), filter }; + return this; + } + + iso(iso: number): this { + this._camera = { ...(this._camera || {}), iso }; + return this; + } + + shutterSpeed(speed: string): this { + this._camera = { ...(this._camera || {}), shutterSpeed: speed }; + return this; + } + + whiteBalance(wb: ImageCamera['whiteBalance']): this { + this._camera = { ...(this._camera || {}), whiteBalance: wb }; + return this; + } + + colorProfile(profile: string): this { + this._camera = { ...(this._camera || {}), colorProfile: profile }; + return this; + } + + // --- Lighting Methods --- + + lighting(settings: ImageLighting): this { + this._lighting = { ...(this._lighting || {}), ...settings }; + return this; + } + + lightingType(type: LightingType | LightingType[]): this { + this._lighting = { ...(this._lighting || {}), type }; + return this; + } + + timeOfDay(time: TimeOfDay): this { + this._lighting = { ...(this._lighting || {}), time }; + return this; + } + + weather(weather: WeatherLighting): this { + this._lighting = { ...(this._lighting || {}), weather }; + return this; + } + + lightDirection(direction: ImageLighting['direction']): this { + this._lighting = { ...(this._lighting || {}), direction }; + return this; + } + + lightIntensity(intensity: ImageLighting['intensity']): this { + this._lighting = { ...(this._lighting || {}), intensity }; + return this; + } + + // --- Composition Methods --- + + composition(settings: ImageComposition): this { + this._composition = { ...(this._composition || {}), ...settings }; + return this; + } + + ruleOfThirds(): this { + this._composition = { ...(this._composition || {}), ruleOfThirds: true }; + return this; + } + + goldenRatio(): this { + this._composition = { ...(this._composition || {}), goldenRatio: true }; + return this; + } + + symmetry(type: ImageComposition['symmetry']): this { + this._composition = { ...(this._composition || {}), symmetry: type }; + return this; + } + + foreground(fg: string): this { + this._composition = { ...(this._composition || {}), foreground: fg }; + return this; + } + + midground(mg: string): this { + this._composition = { ...(this._composition || {}), midground: mg }; + return this; + } + + background(bg: string): this { + this._composition = { ...(this._composition || {}), background: bg }; + return this; + } + + // --- Environment Methods --- + + environment(setting: string | ImageEnvironment): this { + if (typeof setting === 'string') { + this._environment = { ...(this._environment || { setting: '' }), setting }; + } else { + this._environment = { ...(this._environment || { setting: '' }), ...setting }; + } + return this; + } + + location(location: string): this { + this._environment = { ...(this._environment || { setting: '' }), location }; + return this; + } + + props(props: string[]): this { + this._environment = { ...(this._environment || { setting: '' }), props }; + return this; + } + + atmosphere(atmosphere: string): this { + this._environment = { ...(this._environment || { setting: '' }), atmosphere }; + return this; + } + + season(season: ImageEnvironment['season']): this { + this._environment = { ...(this._environment || { setting: '' }), season }; + return this; + } + + // --- Style Methods --- + + style(settings: ImageStyle): this { + this._style = { ...(this._style || {}), ...settings }; + return this; + } + + medium(medium: ArtStyle | ArtStyle[]): this { + this._style = { ...(this._style || {}), medium }; + return this; + } + + artist(artist: string | string[]): this { + this._style = { ...(this._style || {}), artist }; + return this; + } + + influence(influences: string[]): this { + this._style = { ...(this._style || {}), influence: influences }; + return this; + } + + // --- Color Methods --- + + color(settings: ImageColor): this { + this._color = { ...(this._color || {}), ...settings }; + return this; + } + + palette(palette: ColorPalette | ColorPalette[]): this { + this._color = { ...(this._color || {}), palette }; + return this; + } + + primaryColors(colors: string[]): this { + this._color = { ...(this._color || {}), primary: colors }; + return this; + } + + accentColors(colors: string[]): this { + this._color = { ...(this._color || {}), accent: colors }; + return this; + } + + colorGrade(grade: string): this { + this._color = { ...(this._color || {}), grade }; + return this; + } + + // --- Technical Methods --- + + technical(settings: ImageTechnical): this { + this._technical = { ...(this._technical || {}), ...settings }; + return this; + } + + aspectRatio(ratio: AspectRatio): this { + this._technical = { ...(this._technical || {}), aspectRatio: ratio }; + return this; + } + + resolution(resolution: string): this { + this._technical = { ...(this._technical || {}), resolution }; + return this; + } + + quality(quality: ImageTechnical['quality']): this { + this._technical = { ...(this._technical || {}), quality }; + return this; + } + + // --- Mood & Misc --- + + mood(mood: Mood | Mood[]): this { + this._mood = mood; + return this; + } + + negative(items: string[]): this { + this._negative = [...this._negative, ...items]; + return this; + } + + custom(text: string): this { + this._custom.push(text); + return this; + } + + // --- Build Methods --- + + private buildPromptText(): string { + const parts: string[] = []; + + // Subject + if (this._subject) { + let subjectText = this._subject.main; + if (this._subject.count && this._subject.count !== 'single') { + subjectText = `${this._subject.count} ${subjectText}`; + } + if (this._subject.expression) subjectText += `, ${this._subject.expression} expression`; + if (this._subject.pose) subjectText += `, ${this._subject.pose}`; + if (this._subject.action) subjectText += `, ${this._subject.action}`; + if (this._subject.clothing) subjectText += `, wearing ${this._subject.clothing}`; + if (this._subject.accessories?.length) subjectText += `, with ${this._subject.accessories.join(', ')}`; + if (this._subject.details?.length) subjectText += `, ${this._subject.details.join(', ')}`; + parts.push(subjectText); + } + + // Environment + if (this._environment) { + let envText = this._environment.setting; + if (this._environment.location) envText += ` in ${this._environment.location}`; + if (this._environment.atmosphere) envText += `, ${this._environment.atmosphere} atmosphere`; + if (this._environment.season) envText += `, ${this._environment.season}`; + if (this._environment.props?.length) envText += `, with ${this._environment.props.join(', ')}`; + parts.push(envText); + } + + // Composition + if (this._composition) { + const compParts: string[] = []; + if (this._composition.foreground) compParts.push(`foreground: ${this._composition.foreground}`); + if (this._composition.midground) compParts.push(`midground: ${this._composition.midground}`); + if (this._composition.background) compParts.push(`background: ${this._composition.background}`); + if (this._composition.ruleOfThirds) compParts.push('rule of thirds composition'); + if (this._composition.goldenRatio) compParts.push('golden ratio composition'); + if (this._composition.symmetry && this._composition.symmetry !== 'none') { + compParts.push(`${this._composition.symmetry} symmetry`); + } + if (compParts.length) parts.push(compParts.join(', ')); + } + + // Camera + if (this._camera) { + const camParts: string[] = []; + if (this._camera.shot) camParts.push(`${this._camera.shot} shot`); + if (this._camera.angle) camParts.push(`${this._camera.angle}`); + if (this._camera.lens) camParts.push(`${this._camera.lens} lens`); + if (this._camera.focus) camParts.push(`${this._camera.focus} depth of field`); + if (this._camera.aperture) camParts.push(`f/${this._camera.aperture}`); + if (this._camera.filmStock) camParts.push(`shot on ${this._camera.filmStock}`); + if (this._camera.brand) camParts.push(`${this._camera.brand}`); + if (camParts.length) parts.push(camParts.join(', ')); + } + + // Lighting + if (this._lighting) { + const lightParts: string[] = []; + if (this._lighting.type) { + const types = Array.isArray(this._lighting.type) ? this._lighting.type : [this._lighting.type]; + lightParts.push(`${types.join(' and ')} lighting`); + } + if (this._lighting.time) lightParts.push(this._lighting.time); + if (this._lighting.weather) lightParts.push(`${this._lighting.weather} weather`); + if (this._lighting.direction) lightParts.push(`light from ${this._lighting.direction}`); + if (this._lighting.intensity) lightParts.push(`${this._lighting.intensity} light`); + if (lightParts.length) parts.push(lightParts.join(', ')); + } + + // Style + if (this._style) { + const styleParts: string[] = []; + if (this._style.medium) { + const mediums = Array.isArray(this._style.medium) ? this._style.medium : [this._style.medium]; + styleParts.push(mediums.join(', ')); + } + if (this._style.artist) { + const artists = Array.isArray(this._style.artist) ? this._style.artist : [this._style.artist]; + styleParts.push(`in the style of ${artists.join(' and ')}`); + } + if (this._style.era) styleParts.push(this._style.era); + if (this._style.influence?.length) styleParts.push(`influenced by ${this._style.influence.join(', ')}`); + if (this._style.quality?.length) styleParts.push(this._style.quality.join(', ')); + if (styleParts.length) parts.push(styleParts.join(', ')); + } + + // Color + if (this._color) { + const colorParts: string[] = []; + if (this._color.palette) { + const palettes = Array.isArray(this._color.palette) ? this._color.palette : [this._color.palette]; + colorParts.push(`${palettes.join(' and ')} color palette`); + } + if (this._color.primary?.length) colorParts.push(`primary colors: ${this._color.primary.join(', ')}`); + if (this._color.accent?.length) colorParts.push(`accent colors: ${this._color.accent.join(', ')}`); + if (this._color.grade) colorParts.push(`${this._color.grade} color grade`); + if (this._color.temperature) colorParts.push(`${this._color.temperature} tones`); + if (colorParts.length) parts.push(colorParts.join(', ')); + } + + // Mood + if (this._mood) { + const moods = Array.isArray(this._mood) ? this._mood : [this._mood]; + parts.push(`${moods.join(', ')} mood`); + } + + // Technical + if (this._technical) { + const techParts: string[] = []; + if (this._technical.quality) techParts.push(`${this._technical.quality} quality`); + if (this._technical.detail) techParts.push(`${this._technical.detail} detail`); + if (this._technical.resolution) techParts.push(this._technical.resolution); + if (techParts.length) parts.push(techParts.join(', ')); + } + + // Custom + if (this._custom.length) { + parts.push(this._custom.join(', ')); + } + + let prompt = parts.join(', '); + + // Negative prompts + if (this._negative.length) { + prompt += ` --no ${this._negative.join(', ')}`; + } + + // Aspect ratio + if (this._technical?.aspectRatio) { + prompt += ` --ar ${this._technical.aspectRatio}`; + } + + return prompt; + } + + build(): BuiltImagePrompt { + return { + prompt: this.buildPromptText(), + structure: { + subject: this._subject, + camera: this._camera, + lighting: this._lighting, + composition: this._composition, + style: this._style, + color: this._color, + environment: this._environment, + technical: this._technical, + mood: this._mood, + negative: this._negative.length ? this._negative : undefined, + }, + }; + } + + toString(): string { + return this.build().prompt; + } + + toJSON(): string { + return JSON.stringify(this.build().structure, null, 2); + } + + toYAML(): string { + return objectToYaml(this.build().structure); + } + + toMarkdown(): string { + const built = this.build(); + const sections: string[] = ['# Image Prompt\n']; + + sections.push('## Prompt\n```\n' + built.prompt + '\n```\n'); + + if (built.structure.subject) { + sections.push('## Subject\n' + objectToMarkdownList(built.structure.subject)); + } + if (built.structure.environment) { + sections.push('## Environment\n' + objectToMarkdownList(built.structure.environment)); + } + if (built.structure.camera) { + sections.push('## Camera\n' + objectToMarkdownList(built.structure.camera)); + } + if (built.structure.lighting) { + sections.push('## Lighting\n' + objectToMarkdownList(built.structure.lighting)); + } + if (built.structure.composition) { + sections.push('## Composition\n' + objectToMarkdownList(built.structure.composition)); + } + if (built.structure.style) { + sections.push('## Style\n' + objectToMarkdownList(built.structure.style)); + } + if (built.structure.color) { + sections.push('## Color\n' + objectToMarkdownList(built.structure.color)); + } + if (built.structure.technical) { + sections.push('## Technical\n' + objectToMarkdownList(built.structure.technical)); + } + + return sections.join('\n'); + } + + format(fmt: OutputFormat): string { + switch (fmt) { + case 'json': return this.toJSON(); + case 'yaml': return this.toYAML(); + case 'markdown': return this.toMarkdown(); + default: return this.toString(); + } + } +} + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +function objectToYaml(obj: object, indent = 0): string { + const spaces = ' '.repeat(indent); + const lines: string[] = []; + + for (const [key, value] of Object.entries(obj)) { + if (value === undefined || value === null) continue; + + if (Array.isArray(value)) { + if (value.length === 0) continue; + lines.push(`${spaces}${key}:`); + for (const item of value) { + if (typeof item === 'object') { + lines.push(`${spaces} -`); + lines.push(objectToYaml(item as Record, indent + 2).replace(/^/gm, ' ')); + } else { + lines.push(`${spaces} - ${item}`); + } + } + } else if (typeof value === 'object') { + lines.push(`${spaces}${key}:`); + lines.push(objectToYaml(value as Record, indent + 1)); + } else { + lines.push(`${spaces}${key}: ${value}`); + } + } + + return lines.join('\n'); +} + +function objectToMarkdownList(obj: object, indent = 0): string { + const spaces = ' '.repeat(indent); + const lines: string[] = []; + + for (const [key, value] of Object.entries(obj)) { + if (value === undefined || value === null) continue; + + if (Array.isArray(value)) { + lines.push(`${spaces}- **${key}:** ${value.join(', ')}`); + } else if (typeof value === 'object') { + lines.push(`${spaces}- **${key}:**`); + lines.push(objectToMarkdownList(value as Record, indent + 1)); + } else { + lines.push(`${spaces}- **${key}:** ${value}`); + } + } + + return lines.join('\n'); +} + +// ============================================================================ +// FACTORY FUNCTIONS +// ============================================================================ + +/** + * Create a new image prompt builder + */ +export function image(): ImagePromptBuilder { + return new ImagePromptBuilder(); +} diff --git a/packages/prompts.chat/src/builder/video.ts b/packages/prompts.chat/src/builder/video.ts new file mode 100644 index 00000000..cb96dee6 --- /dev/null +++ b/packages/prompts.chat/src/builder/video.ts @@ -0,0 +1,819 @@ +/** + * Video Prompt Builder - Comprehensive video generation prompt builder + * + * Based on OpenAI Sora, Runway, and other video generation best practices. + * + * @example + * ```ts + * import { video } from 'prompts.chat/builder'; + * + * const prompt = video() + * .scene("A samurai walks through a bamboo forest") + * .camera({ movement: "tracking", angle: "low" }) + * .lighting({ time: "golden-hour", type: "natural" }) + * .duration(5) + * .build(); + * ``` + */ + +import type { + CameraAngle, ShotType, LensType, CameraMovement, + LightingType, TimeOfDay, WeatherLighting, + ArtStyle, ColorPalette, Mood, VideoTransition, VideoPacing, + OutputFormat, CameraBrand, CameraModel, SensorFormat, + LensBrand, LensModel, CameraRig, GimbalModel, FilterType, + FilmStock, BokehStyle, +} from './media'; + +// ============================================================================ +// VIDEO-SPECIFIC TYPES +// ============================================================================ + +export interface VideoScene { + description: string; + setting?: string; + timeOfDay?: TimeOfDay; + weather?: WeatherLighting; + atmosphere?: string; +} + +export interface VideoSubject { + main: string; + appearance?: string; + clothing?: string; + age?: string; + gender?: string; + count?: number | 'single' | 'couple' | 'group' | 'crowd'; +} + +export interface VideoCamera { + // Framing + shot?: ShotType; + angle?: CameraAngle; + + // Camera Body + brand?: CameraBrand; + model?: CameraModel; + sensor?: SensorFormat; + + // Lens + lens?: LensType; + lensModel?: LensModel; + lensBrand?: LensBrand; + focalLength?: string; + anamorphic?: boolean; + anamorphicRatio?: '1.33x' | '1.5x' | '1.8x' | '2x'; + + // Focus + focus?: 'shallow' | 'deep' | 'rack-focus' | 'pull-focus' | 'split-diopter'; + aperture?: string; + bokeh?: BokehStyle; + + // Movement + movement?: CameraMovement; + movementSpeed?: 'slow' | 'medium' | 'fast'; + movementDirection?: 'left' | 'right' | 'forward' | 'backward' | 'up' | 'down' | 'arc-left' | 'arc-right'; + + // Rig & Stabilization + rig?: CameraRig; + gimbal?: GimbalModel; + platform?: 'handheld' | 'steadicam' | 'tripod' | 'drone' | 'crane' | 'gimbal' | 'slider' | 'dolly' | 'technocrane' | 'russian-arm' | 'fpv-drone'; + + // Technical + shutterAngle?: number; + frameRate?: 24 | 25 | 30 | 48 | 60 | 120 | 240; + slowMotion?: boolean; + filter?: FilterType | FilterType[]; + + // Film Look + filmStock?: FilmStock; + filmGrain?: 'none' | 'subtle' | 'moderate' | 'heavy'; + halation?: boolean; +} + +export interface VideoLighting { + type?: LightingType | LightingType[]; + time?: TimeOfDay; + weather?: WeatherLighting; + direction?: 'front' | 'side' | 'back' | 'top' | 'three-quarter'; + intensity?: 'soft' | 'medium' | 'hard' | 'dramatic'; + sources?: string[]; + color?: string; +} + +export interface VideoAction { + beat: number; + action: string; + duration?: number; + timing?: 'start' | 'middle' | 'end'; +} + +export interface VideoMotion { + subject?: string; + type?: 'walk' | 'run' | 'gesture' | 'turn' | 'look' | 'reach' | 'sit' | 'stand' | 'custom'; + direction?: 'left' | 'right' | 'forward' | 'backward' | 'up' | 'down'; + speed?: 'slow' | 'normal' | 'fast'; + beats?: string[]; +} + +export interface VideoStyle { + format?: string; + era?: string; + filmStock?: string; + look?: ArtStyle | ArtStyle[]; + grade?: string; + reference?: string[]; +} + +export interface VideoColor { + palette?: ColorPalette | ColorPalette[]; + anchors?: string[]; + temperature?: 'warm' | 'neutral' | 'cool'; + grade?: string; +} + +export interface VideoAudio { + diegetic?: string[]; + ambient?: string; + dialogue?: string; + music?: string; + soundEffects?: string[]; + mix?: string; +} + +export interface VideoTechnical { + duration?: number; + resolution?: '480p' | '720p' | '1080p' | '4K'; + fps?: 24 | 30 | 60; + aspectRatio?: '16:9' | '9:16' | '1:1' | '4:3' | '21:9'; + shutterAngle?: number; +} + +export interface VideoShot { + timestamp?: string; + name?: string; + camera: VideoCamera; + action?: string; + purpose?: string; +} + +export interface BuiltVideoPrompt { + prompt: string; + structure: { + scene?: VideoScene; + subject?: VideoSubject; + camera?: VideoCamera; + lighting?: VideoLighting; + actions?: VideoAction[]; + motion?: VideoMotion; + style?: VideoStyle; + color?: VideoColor; + audio?: VideoAudio; + technical?: VideoTechnical; + shots?: VideoShot[]; + mood?: Mood | Mood[]; + pacing?: VideoPacing; + transitions?: VideoTransition[]; + }; +} + +// ============================================================================ +// VIDEO PROMPT BUILDER +// ============================================================================ + +export class VideoPromptBuilder { + private _scene?: VideoScene; + private _subject?: VideoSubject; + private _camera?: VideoCamera; + private _lighting?: VideoLighting; + private _actions: VideoAction[] = []; + private _motion?: VideoMotion; + private _style?: VideoStyle; + private _color?: VideoColor; + private _audio?: VideoAudio; + private _technical?: VideoTechnical; + private _shots: VideoShot[] = []; + private _mood?: Mood | Mood[]; + private _pacing?: VideoPacing; + private _transitions: VideoTransition[] = []; + private _custom: string[] = []; + + // --- Scene Methods --- + + scene(description: string | VideoScene): this { + if (typeof description === 'string') { + this._scene = { ...(this._scene || { description: '' }), description }; + } else { + this._scene = { ...(this._scene || { description: '' }), ...description }; + } + return this; + } + + setting(setting: string): this { + this._scene = { ...(this._scene || { description: '' }), setting }; + return this; + } + + // --- Subject Methods --- + + subject(main: string | VideoSubject): this { + if (typeof main === 'string') { + this._subject = { ...(this._subject || { main: '' }), main }; + } else { + this._subject = { ...(this._subject || { main: '' }), ...main }; + } + return this; + } + + appearance(appearance: string): this { + this._subject = { ...(this._subject || { main: '' }), appearance }; + return this; + } + + clothing(clothing: string): this { + this._subject = { ...(this._subject || { main: '' }), clothing }; + return this; + } + + // --- Camera Methods --- + + camera(settings: VideoCamera): this { + this._camera = { ...(this._camera || {}), ...settings }; + return this; + } + + shot(shot: ShotType): this { + this._camera = { ...(this._camera || {}), shot }; + return this; + } + + angle(angle: CameraAngle): this { + this._camera = { ...(this._camera || {}), angle }; + return this; + } + + movement(movement: CameraMovement): this { + this._camera = { ...(this._camera || {}), movement }; + return this; + } + + lens(lens: LensType): this { + this._camera = { ...(this._camera || {}), lens }; + return this; + } + + platform(platform: VideoCamera['platform']): this { + this._camera = { ...(this._camera || {}), platform }; + return this; + } + + cameraSpeed(speed: VideoCamera['movementSpeed']): this { + this._camera = { ...(this._camera || {}), movementSpeed: speed }; + return this; + } + + movementDirection(direction: VideoCamera['movementDirection']): this { + this._camera = { ...(this._camera || {}), movementDirection: direction }; + return this; + } + + rig(rig: CameraRig): this { + this._camera = { ...(this._camera || {}), rig }; + return this; + } + + gimbal(gimbal: GimbalModel): this { + this._camera = { ...(this._camera || {}), gimbal }; + return this; + } + + cameraBrand(brand: CameraBrand): this { + this._camera = { ...(this._camera || {}), brand }; + return this; + } + + cameraModel(model: CameraModel): this { + this._camera = { ...(this._camera || {}), model }; + return this; + } + + sensor(sensor: SensorFormat): this { + this._camera = { ...(this._camera || {}), sensor }; + return this; + } + + lensModel(model: LensModel): this { + this._camera = { ...(this._camera || {}), lensModel: model }; + return this; + } + + lensBrand(brand: LensBrand): this { + this._camera = { ...(this._camera || {}), lensBrand: brand }; + return this; + } + + focalLength(length: string): this { + this._camera = { ...(this._camera || {}), focalLength: length }; + return this; + } + + anamorphic(ratio?: VideoCamera['anamorphicRatio']): this { + this._camera = { ...(this._camera || {}), anamorphic: true, anamorphicRatio: ratio }; + return this; + } + + aperture(aperture: string): this { + this._camera = { ...(this._camera || {}), aperture }; + return this; + } + + frameRate(fps: VideoCamera['frameRate']): this { + this._camera = { ...(this._camera || {}), frameRate: fps }; + return this; + } + + slowMotion(enabled = true): this { + this._camera = { ...(this._camera || {}), slowMotion: enabled }; + return this; + } + + shutterAngle(angle: number): this { + this._camera = { ...(this._camera || {}), shutterAngle: angle }; + return this; + } + + filter(filter: FilterType | FilterType[]): this { + this._camera = { ...(this._camera || {}), filter }; + return this; + } + + filmStock(stock: FilmStock): this { + this._camera = { ...(this._camera || {}), filmStock: stock }; + return this; + } + + filmGrain(grain: VideoCamera['filmGrain']): this { + this._camera = { ...(this._camera || {}), filmGrain: grain }; + return this; + } + + halation(enabled = true): this { + this._camera = { ...(this._camera || {}), halation: enabled }; + return this; + } + + // --- Lighting Methods --- + + lighting(settings: VideoLighting): this { + this._lighting = { ...(this._lighting || {}), ...settings }; + return this; + } + + lightingType(type: LightingType | LightingType[]): this { + this._lighting = { ...(this._lighting || {}), type }; + return this; + } + + timeOfDay(time: TimeOfDay): this { + this._lighting = { ...(this._lighting || {}), time }; + this._scene = { ...(this._scene || { description: '' }), timeOfDay: time }; + return this; + } + + weather(weather: WeatherLighting): this { + this._lighting = { ...(this._lighting || {}), weather }; + this._scene = { ...(this._scene || { description: '' }), weather }; + return this; + } + + // --- Action & Motion Methods --- + + action(action: string, options: Partial> = {}): this { + this._actions.push({ + beat: this._actions.length + 1, + action, + ...options, + }); + return this; + } + + actions(actions: string[]): this { + actions.forEach((a, i) => this._actions.push({ beat: i + 1, action: a })); + return this; + } + + motion(settings: VideoMotion): this { + this._motion = { ...(this._motion || {}), ...settings }; + return this; + } + + motionBeats(beats: string[]): this { + this._motion = { ...(this._motion || {}), beats }; + return this; + } + + // --- Style Methods --- + + style(settings: VideoStyle): this { + this._style = { ...(this._style || {}), ...settings }; + return this; + } + + format(format: string): this { + this._style = { ...(this._style || {}), format }; + return this; + } + + era(era: string): this { + this._style = { ...(this._style || {}), era }; + return this; + } + + styleFilmStock(stock: string): this { + this._style = { ...(this._style || {}), filmStock: stock }; + return this; + } + + look(look: ArtStyle | ArtStyle[]): this { + this._style = { ...(this._style || {}), look }; + return this; + } + + reference(references: string[]): this { + this._style = { ...(this._style || {}), reference: references }; + return this; + } + + // --- Color Methods --- + + color(settings: VideoColor): this { + this._color = { ...(this._color || {}), ...settings }; + return this; + } + + palette(palette: ColorPalette | ColorPalette[]): this { + this._color = { ...(this._color || {}), palette }; + return this; + } + + colorAnchors(anchors: string[]): this { + this._color = { ...(this._color || {}), anchors }; + return this; + } + + colorGrade(grade: string): this { + this._color = { ...(this._color || {}), grade }; + return this; + } + + // --- Audio Methods --- + + audio(settings: VideoAudio): this { + this._audio = { ...(this._audio || {}), ...settings }; + return this; + } + + dialogue(dialogue: string): this { + this._audio = { ...(this._audio || {}), dialogue }; + return this; + } + + ambient(ambient: string): this { + this._audio = { ...(this._audio || {}), ambient }; + return this; + } + + diegetic(sounds: string[]): this { + this._audio = { ...(this._audio || {}), diegetic: sounds }; + return this; + } + + soundEffects(effects: string[]): this { + this._audio = { ...(this._audio || {}), soundEffects: effects }; + return this; + } + + music(music: string): this { + this._audio = { ...(this._audio || {}), music }; + return this; + } + + // --- Technical Methods --- + + technical(settings: VideoTechnical): this { + this._technical = { ...(this._technical || {}), ...settings }; + return this; + } + + duration(seconds: number): this { + this._technical = { ...(this._technical || {}), duration: seconds }; + return this; + } + + resolution(res: VideoTechnical['resolution']): this { + this._technical = { ...(this._technical || {}), resolution: res }; + return this; + } + + fps(fps: VideoTechnical['fps']): this { + this._technical = { ...(this._technical || {}), fps }; + return this; + } + + aspectRatio(ratio: VideoTechnical['aspectRatio']): this { + this._technical = { ...(this._technical || {}), aspectRatio: ratio }; + return this; + } + + // --- Shot List Methods --- + + addShot(shot: VideoShot): this { + this._shots.push(shot); + return this; + } + + shotList(shots: VideoShot[]): this { + this._shots = [...this._shots, ...shots]; + return this; + } + + // --- Mood & Pacing --- + + mood(mood: Mood | Mood[]): this { + this._mood = mood; + return this; + } + + pacing(pacing: VideoPacing): this { + this._pacing = pacing; + return this; + } + + transition(transition: VideoTransition): this { + this._transitions.push(transition); + return this; + } + + transitions(transitions: VideoTransition[]): this { + this._transitions = [...this._transitions, ...transitions]; + return this; + } + + custom(text: string): this { + this._custom.push(text); + return this; + } + + // --- Build Methods --- + + private buildPromptText(): string { + const sections: string[] = []; + + // Scene description + if (this._scene) { + let sceneText = this._scene.description; + if (this._scene.setting) sceneText = `${this._scene.setting}. ${sceneText}`; + if (this._scene.atmosphere) sceneText += `, ${this._scene.atmosphere} atmosphere`; + sections.push(sceneText); + } + + // Subject + if (this._subject) { + let subjectText = this._subject.main; + if (this._subject.appearance) subjectText += `, ${this._subject.appearance}`; + if (this._subject.clothing) subjectText += `, wearing ${this._subject.clothing}`; + sections.push(subjectText); + } + + // Camera & Cinematography + const cinematography: string[] = []; + if (this._camera) { + if (this._camera.shot) cinematography.push(`${this._camera.shot} shot`); + if (this._camera.angle) cinematography.push(this._camera.angle); + if (this._camera.movement) cinematography.push(`${this._camera.movement} camera`); + if (this._camera.lens) cinematography.push(`${this._camera.lens} lens`); + if (this._camera.platform) cinematography.push(this._camera.platform); + if (this._camera.focus) cinematography.push(`${this._camera.focus} focus`); + } + if (cinematography.length) { + sections.push(`Cinematography: ${cinematography.join(', ')}`); + } + + // Lighting + if (this._lighting) { + const lightParts: string[] = []; + if (this._lighting.type) { + const types = Array.isArray(this._lighting.type) ? this._lighting.type : [this._lighting.type]; + lightParts.push(`${types.join(' and ')} lighting`); + } + if (this._lighting.time) lightParts.push(this._lighting.time); + if (this._lighting.weather) lightParts.push(`${this._lighting.weather} weather`); + if (this._lighting.intensity) lightParts.push(`${this._lighting.intensity} light`); + if (this._lighting.sources?.length) lightParts.push(`light sources: ${this._lighting.sources.join(', ')}`); + if (lightParts.length) sections.push(`Lighting: ${lightParts.join(', ')}`); + } + + // Actions + if (this._actions.length) { + const actionText = this._actions.map(a => `- ${a.action}`).join('\n'); + sections.push(`Actions:\n${actionText}`); + } + + // Motion + if (this._motion?.beats?.length) { + sections.push(`Motion beats: ${this._motion.beats.join(', ')}`); + } + + // Style + if (this._style) { + const styleParts: string[] = []; + if (this._style.format) styleParts.push(this._style.format); + if (this._style.era) styleParts.push(this._style.era); + if (this._style.filmStock) styleParts.push(`shot on ${this._style.filmStock}`); + if (this._style.look) { + const looks = Array.isArray(this._style.look) ? this._style.look : [this._style.look]; + styleParts.push(looks.join(', ')); + } + if (styleParts.length) sections.push(`Style: ${styleParts.join(', ')}`); + } + + // Color + if (this._color) { + const colorParts: string[] = []; + if (this._color.palette) { + const palettes = Array.isArray(this._color.palette) ? this._color.palette : [this._color.palette]; + colorParts.push(`${palettes.join(' and ')} palette`); + } + if (this._color.anchors?.length) colorParts.push(`color anchors: ${this._color.anchors.join(', ')}`); + if (this._color.grade) colorParts.push(this._color.grade); + if (colorParts.length) sections.push(`Color: ${colorParts.join(', ')}`); + } + + // Audio + if (this._audio) { + const audioParts: string[] = []; + if (this._audio.dialogue) audioParts.push(`Dialogue: "${this._audio.dialogue}"`); + if (this._audio.ambient) audioParts.push(`Ambient: ${this._audio.ambient}`); + if (this._audio.diegetic?.length) audioParts.push(`Diegetic sounds: ${this._audio.diegetic.join(', ')}`); + if (this._audio.music) audioParts.push(`Music: ${this._audio.music}`); + if (audioParts.length) sections.push(`Audio:\n${audioParts.join('\n')}`); + } + + // Mood & Pacing + if (this._mood) { + const moods = Array.isArray(this._mood) ? this._mood : [this._mood]; + sections.push(`Mood: ${moods.join(', ')}`); + } + if (this._pacing) { + sections.push(`Pacing: ${this._pacing}`); + } + + // Custom + if (this._custom.length) { + sections.push(this._custom.join('\n')); + } + + return sections.join('\n\n'); + } + + build(): BuiltVideoPrompt { + return { + prompt: this.buildPromptText(), + structure: { + scene: this._scene, + subject: this._subject, + camera: this._camera, + lighting: this._lighting, + actions: this._actions.length ? this._actions : undefined, + motion: this._motion, + style: this._style, + color: this._color, + audio: this._audio, + technical: this._technical, + shots: this._shots.length ? this._shots : undefined, + mood: this._mood, + pacing: this._pacing, + transitions: this._transitions.length ? this._transitions : undefined, + }, + }; + } + + toString(): string { + return this.build().prompt; + } + + toJSON(): string { + return JSON.stringify(this.build().structure, null, 2); + } + + toYAML(): string { + return objectToYaml(this.build().structure); + } + + toMarkdown(): string { + const built = this.build(); + const sections: string[] = ['# Video Prompt\n']; + + sections.push('## Prompt\n```\n' + built.prompt + '\n```\n'); + + if (built.structure.scene) { + sections.push('## Scene\n' + objectToMarkdownList(built.structure.scene)); + } + if (built.structure.subject) { + sections.push('## Subject\n' + objectToMarkdownList(built.structure.subject)); + } + if (built.structure.camera) { + sections.push('## Camera\n' + objectToMarkdownList(built.structure.camera)); + } + if (built.structure.lighting) { + sections.push('## Lighting\n' + objectToMarkdownList(built.structure.lighting)); + } + if (built.structure.actions) { + sections.push('## Actions\n' + built.structure.actions.map(a => `- **Beat ${a.beat}:** ${a.action}`).join('\n')); + } + if (built.structure.style) { + sections.push('## Style\n' + objectToMarkdownList(built.structure.style)); + } + if (built.structure.audio) { + sections.push('## Audio\n' + objectToMarkdownList(built.structure.audio)); + } + if (built.structure.technical) { + sections.push('## Technical\n' + objectToMarkdownList(built.structure.technical)); + } + + return sections.join('\n'); + } + + outputFormat(fmt: OutputFormat): string { + switch (fmt) { + case 'json': return this.toJSON(); + case 'yaml': return this.toYAML(); + case 'markdown': return this.toMarkdown(); + default: return this.toString(); + } + } +} + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +function objectToYaml(obj: object, indent = 0): string { + const spaces = ' '.repeat(indent); + const lines: string[] = []; + + for (const [key, value] of Object.entries(obj)) { + if (value === undefined || value === null) continue; + + if (Array.isArray(value)) { + if (value.length === 0) continue; + lines.push(`${spaces}${key}:`); + for (const item of value) { + if (typeof item === 'object') { + lines.push(`${spaces} -`); + lines.push(objectToYaml(item, indent + 2).replace(/^/gm, ' ')); + } else { + lines.push(`${spaces} - ${item}`); + } + } + } else if (typeof value === 'object') { + lines.push(`${spaces}${key}:`); + lines.push(objectToYaml(value, indent + 1)); + } else { + lines.push(`${spaces}${key}: ${value}`); + } + } + + return lines.join('\n'); +} + +function objectToMarkdownList(obj: object, indent = 0): string { + const spaces = ' '.repeat(indent); + const lines: string[] = []; + + for (const [key, value] of Object.entries(obj)) { + if (value === undefined || value === null) continue; + + if (Array.isArray(value)) { + lines.push(`${spaces}- **${key}:** ${value.join(', ')}`); + } else if (typeof value === 'object') { + lines.push(`${spaces}- **${key}:**`); + lines.push(objectToMarkdownList(value, indent + 1)); + } else { + lines.push(`${spaces}- **${key}:** ${value}`); + } + } + + return lines.join('\n'); +} + +// ============================================================================ +// FACTORY FUNCTION +// ============================================================================ + +/** + * Create a new video prompt builder + */ +export function video(): VideoPromptBuilder { + return new VideoPromptBuilder(); +} diff --git a/packages/prompts.chat/src/index.ts b/packages/prompts.chat/src/index.ts new file mode 100644 index 00000000..c9f0961c --- /dev/null +++ b/packages/prompts.chat/src/index.ts @@ -0,0 +1,100 @@ +/** + * prompts.chat - Developer toolkit for AI prompts + * + * @example + * ```ts + * import { variables, similarity, builder, quality } from 'prompts.chat'; + * + * // Detect and normalize variables + * const vars = variables.detect("Hello {{name}}, you are [ROLE]"); + * const normalized = variables.normalize("Hello {{name}}"); + * // → "Hello ${name}" + * + * // Check similarity + * const score = similarity.calculate(prompt1, prompt2); + * const isDupe = similarity.isDuplicate(prompt1, prompt2); + * + * // Build structured prompts + * const prompt = builder() + * .role("Senior Developer") + * .task("Review code") + * .build(); + * + * // Check quality locally + * const result = quality.check("Act as a developer..."); + * + * ``` + */ + +// Module exports +export * as variables from './variables'; +export * as similarity from './similarity'; +export * as quality from './quality'; +export * as parser from './parser'; + +// Builder exports (special handling for fluent API) +export { + builder, + fromPrompt, + templates, + PromptBuilder, + video, + audio, + image, + chat, + chatPresets, + VideoPromptBuilder, + AudioPromptBuilder, + ImagePromptBuilder, + ChatPromptBuilder, + type BuiltPrompt, + type PromptVariable, + type BuiltVideoPrompt, + type BuiltAudioPrompt, + type BuiltImagePrompt, + type BuiltChatPrompt, +} from './builder'; + +// Variable utilities +export { + detectVariables, + convertToSupportedFormat, + convertAllVariables, + extractVariables, + compile, + normalize as normalizeVariables, + detect as detectVars, + type DetectedVariable, + type VariablePattern, +} from './variables'; + +// Similarity utilities +export { + calculateSimilarity, + isSimilarContent, + normalizeContent, + getContentFingerprint, + findDuplicates, + deduplicate, +} from './similarity'; + +// Quality utilities +export { + check as checkQuality, + validate as validatePrompt, + isValid as isValidPrompt, + getSuggestions, + type QualityResult, + type QualityIssue, +} from './quality'; + +// Parser utilities +export { + parse as parsePrompt, + toYaml, + toJson, + getSystemPrompt, + interpolate, + type ParsedPrompt, + type PromptMessage, +} from './parser'; diff --git a/packages/prompts.chat/src/parser/index.ts b/packages/prompts.chat/src/parser/index.ts new file mode 100644 index 00000000..e810bd80 --- /dev/null +++ b/packages/prompts.chat/src/parser/index.ts @@ -0,0 +1,389 @@ +/** + * Prompt Parser - Parse and load prompt files in various formats + * + * Supports: + * - .prompt.yml / .prompt.yaml (YAML format) + * - .prompt.json (JSON format) + * - .prompt.md (Markdown with frontmatter) + * - .txt (Plain text) + * + * @example + * ```ts + * import { parser } from 'prompts.chat'; + * + * const prompt = parser.parse(` + * name: Code Review + * messages: + * - role: system + * content: You are a code reviewer. + * `); + * ``` + */ + +export interface PromptMessage { + role: 'system' | 'user' | 'assistant'; + content: string; +} + +export interface ParsedPrompt { + name?: string; + description?: string; + model?: string; + modelParameters?: { + temperature?: number; + maxTokens?: number; + topP?: number; + frequencyPenalty?: number; + presencePenalty?: number; + }; + messages: PromptMessage[]; + variables?: Record; + metadata?: Record; +} + +/** + * Parse YAML content + * Note: This is a simple YAML parser for common prompt file structures + * For full YAML support, the consuming project should use a proper YAML library + */ +function parseSimpleYaml(content: string): Record { + const result: Record = {}; + const lines = content.split('\n'); + + let currentKey: string | null = null; + let currentValue: unknown = null; + let inArray = false; + let inMultiline = false; + let multilineContent = ''; + let arrayItems: unknown[] = []; + let indent = 0; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmed = line.trim(); + + // Skip empty lines and comments + if (!trimmed || trimmed.startsWith('#')) { + if (inMultiline) { + multilineContent += '\n'; + } + continue; + } + + // Handle multiline content (|) + if (inMultiline) { + const lineIndent = line.search(/\S/); + if (lineIndent > indent) { + multilineContent += (multilineContent ? '\n' : '') + line.slice(indent + 2); + continue; + } else { + // End of multiline + if (inArray && currentKey) { + const lastItem = arrayItems[arrayItems.length - 1] as Record; + if (lastItem && typeof lastItem === 'object') { + const keys = Object.keys(lastItem); + const lastKey = keys[keys.length - 1]; + lastItem[lastKey] = multilineContent.trim(); + } + } else if (currentKey) { + result[currentKey] = multilineContent.trim(); + } + inMultiline = false; + multilineContent = ''; + } + } + + // Handle array items + if (trimmed.startsWith('- ')) { + if (!inArray && currentKey) { + inArray = true; + arrayItems = []; + } + + const itemContent = trimmed.slice(2); + + // Check if it's a key-value pair + const kvMatch = itemContent.match(/^(\w+):\s*(.*)$/); + if (kvMatch) { + const obj: Record = {}; + obj[kvMatch[1]] = kvMatch[2] === '|' ? '' : (kvMatch[2] || ''); + + if (kvMatch[2] === '|') { + inMultiline = true; + indent = line.search(/\S/); + multilineContent = ''; + } + + arrayItems.push(obj); + } else { + arrayItems.push(itemContent); + } + continue; + } + + // Handle nested object properties in arrays + if (inArray && line.startsWith(' ')) { + const propMatch = trimmed.match(/^(\w+):\s*(.*)$/); + if (propMatch && arrayItems.length > 0) { + const lastItem = arrayItems[arrayItems.length - 1]; + if (typeof lastItem === 'object' && lastItem !== null) { + (lastItem as Record)[propMatch[1]] = + propMatch[2] === '|' ? '' : (propMatch[2] || ''); + + if (propMatch[2] === '|') { + inMultiline = true; + indent = line.search(/\S/); + multilineContent = ''; + } + } + } + continue; + } + + // End array if we're back to root level + if (inArray && !line.startsWith(' ') && !line.startsWith('\t')) { + if (currentKey) { + result[currentKey] = arrayItems; + } + inArray = false; + arrayItems = []; + } + + // Handle key-value pairs + const match = trimmed.match(/^(\w+):\s*(.*)$/); + if (match) { + currentKey = match[1]; + const value = match[2]; + + if (value === '' || value === '|' || value === '>') { + // Multiline or nested content + if (value === '|' || value === '>') { + inMultiline = true; + indent = line.search(/\S/); + multilineContent = ''; + } + } else if (value.startsWith('"') && value.endsWith('"')) { + result[currentKey] = value.slice(1, -1); + } else if (value.startsWith("'") && value.endsWith("'")) { + result[currentKey] = value.slice(1, -1); + } else if (value === 'true') { + result[currentKey] = true; + } else if (value === 'false') { + result[currentKey] = false; + } else if (!isNaN(Number(value))) { + result[currentKey] = Number(value); + } else { + result[currentKey] = value; + } + } + } + + // Handle remaining array + if (inArray && currentKey) { + result[currentKey] = arrayItems; + } + + // Handle remaining multiline + if (inMultiline && currentKey) { + result[currentKey] = multilineContent.trim(); + } + + return result; +} + +/** + * Parse JSON content + */ +function parseJson(content: string): Record { + return JSON.parse(content); +} + +/** + * Parse Markdown with frontmatter + */ +function parseMarkdown(content: string): Record { + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); + + if (frontmatterMatch) { + const frontmatter = parseSimpleYaml(frontmatterMatch[1]); + const body = frontmatterMatch[2].trim(); + + return { + ...frontmatter, + messages: [{ role: 'system', content: body }], + }; + } + + // No frontmatter, treat entire content as system message + return { + messages: [{ role: 'system', content: content.trim() }], + }; +} + +/** + * Normalize parsed data to ParsedPrompt format + */ +function normalize(data: Record): ParsedPrompt { + const messages: PromptMessage[] = []; + + // Handle messages array + if (Array.isArray(data.messages)) { + for (const msg of data.messages) { + if (typeof msg === 'object' && msg !== null) { + const m = msg as Record; + messages.push({ + role: (m.role as PromptMessage['role']) || 'user', + content: String(m.content || ''), + }); + } + } + } + + // Handle single content field + if (messages.length === 0 && typeof data.content === 'string') { + messages.push({ role: 'system', content: data.content }); + } + + // Handle prompt field (alias for content) + if (messages.length === 0 && typeof data.prompt === 'string') { + messages.push({ role: 'system', content: data.prompt }); + } + + return { + name: data.name as string | undefined, + description: data.description as string | undefined, + model: data.model as string | undefined, + modelParameters: data.modelParameters as ParsedPrompt['modelParameters'], + messages, + variables: data.variables as ParsedPrompt['variables'], + metadata: data.metadata as Record, + }; +} + +/** + * Parse prompt content in various formats + */ +export function parse(content: string, format?: 'yaml' | 'json' | 'markdown' | 'text'): ParsedPrompt { + const trimmed = content.trim(); + + // Auto-detect format if not specified + if (!format) { + if (trimmed.startsWith('{')) { + format = 'json'; + } else if (trimmed.startsWith('---')) { + format = 'markdown'; + } else if (trimmed.includes(':') && (trimmed.includes('\n ') || trimmed.includes('\n-'))) { + format = 'yaml'; + } else { + format = 'text'; + } + } + + let data: Record; + + switch (format) { + case 'json': + data = parseJson(trimmed); + break; + case 'yaml': + data = parseSimpleYaml(trimmed); + break; + case 'markdown': + data = parseMarkdown(trimmed); + break; + case 'text': + default: + data = { messages: [{ role: 'system', content: trimmed }] }; + break; + } + + return normalize(data); +} + +/** + * Serialize a ParsedPrompt to YAML format + */ +export function toYaml(prompt: ParsedPrompt): string { + const lines: string[] = []; + + if (prompt.name) { + lines.push(`name: ${prompt.name}`); + } + + if (prompt.description) { + lines.push(`description: ${prompt.description}`); + } + + if (prompt.model) { + lines.push(`model: ${prompt.model}`); + } + + if (prompt.modelParameters) { + lines.push('modelParameters:'); + for (const [key, value] of Object.entries(prompt.modelParameters)) { + if (value !== undefined) { + lines.push(` ${key}: ${value}`); + } + } + } + + if (prompt.messages.length > 0) { + lines.push('messages:'); + for (const msg of prompt.messages) { + lines.push(` - role: ${msg.role}`); + if (msg.content.includes('\n')) { + lines.push(' content: |'); + for (const line of msg.content.split('\n')) { + lines.push(` ${line}`); + } + } else { + lines.push(` content: "${msg.content.replace(/"/g, '\\"')}"`); + } + } + } + + return lines.join('\n'); +} + +/** + * Serialize a ParsedPrompt to JSON format + */ +export function toJson(prompt: ParsedPrompt, pretty: boolean = true): string { + return JSON.stringify(prompt, null, pretty ? 2 : 0); +} + +/** + * Get the system message content from a parsed prompt + */ +export function getSystemPrompt(prompt: ParsedPrompt): string { + const systemMessage = prompt.messages.find(m => m.role === 'system'); + return systemMessage?.content || ''; +} + +/** + * Interpolate variables in a prompt + */ +export function interpolate( + prompt: ParsedPrompt, + values: Record +): ParsedPrompt { + const interpolateString = (str: string): string => { + return str.replace(/\{\{(\w+)\}\}/g, (match, key) => { + if (key in values) return values[key]; + if (prompt.variables?.[key]?.default) return prompt.variables[key].default!; + return match; + }); + }; + + return { + ...prompt, + messages: prompt.messages.map(msg => ({ + ...msg, + content: interpolateString(msg.content), + })), + }; +} diff --git a/packages/prompts.chat/src/quality/index.ts b/packages/prompts.chat/src/quality/index.ts new file mode 100644 index 00000000..08de2ab8 --- /dev/null +++ b/packages/prompts.chat/src/quality/index.ts @@ -0,0 +1,307 @@ +/** + * Prompt Quality Checker - Local validation for prompt quality + * + * @example + * ```ts + * import { quality } from 'prompts.chat'; + * + * const result = quality.check("Act as a developer..."); + * console.log(result.score); // 0.85 + * console.log(result.issues); // [] + * ``` + */ + +export interface QualityIssue { + type: 'error' | 'warning' | 'suggestion'; + code: string; + message: string; + position?: { start: number; end: number }; +} + +export interface QualityResult { + valid: boolean; + score: number; // 0-1 + issues: QualityIssue[]; + stats: { + characterCount: number; + wordCount: number; + sentenceCount: number; + variableCount: number; + hasRole: boolean; + hasTask: boolean; + hasConstraints: boolean; + hasExamples: boolean; + }; +} + +// Minimum thresholds +const MIN_CHAR_COUNT = 20; +const MIN_WORD_COUNT = 5; +const OPTIMAL_MIN_WORDS = 20; +const OPTIMAL_MAX_WORDS = 2000; + +/** + * Check if text looks like gibberish + */ +function isGibberish(text: string): boolean { + // Check for repeated characters + if (/(.)\1{4,}/.test(text)) return true; + + // Check for keyboard patterns + const keyboardPatterns = ['qwerty', 'asdfgh', 'zxcvbn', 'qwertz', 'azerty']; + const lower = text.toLowerCase(); + if (keyboardPatterns.some(p => lower.includes(p))) return true; + + // Check consonant/vowel ratio (gibberish often has unusual ratios) + const vowels = (text.match(/[aeiouAEIOU]/g) || []).length; + const consonants = (text.match(/[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]/g) || []).length; + + if (consonants > 0 && vowels / consonants < 0.1) return true; + + return false; +} + +/** + * Detect common prompt patterns + */ +function detectPatterns(text: string): { + hasRole: boolean; + hasTask: boolean; + hasConstraints: boolean; + hasExamples: boolean; +} { + const lower = text.toLowerCase(); + + return { + hasRole: /\b(act as|you are|imagine you|pretend to be|role:|persona:)\b/i.test(text), + hasTask: /\b(your task|you (will|should|must)|please|help me|i need|i want you to)\b/i.test(text), + hasConstraints: /\b(do not|don't|never|always|must|should not|avoid|only|limit)\b/i.test(text) || + /\b(rule|constraint|requirement|guideline)\b/i.test(lower), + hasExamples: /\b(example|for instance|such as|e\.g\.|like this)\b/i.test(lower) || + /```[\s\S]*```/.test(text), + }; +} + +/** + * Count variables in the prompt + */ +function countVariables(text: string): number { + // Match various variable formats + const patterns = [ + /\$\{[^}]+\}/g, // ${var} + /\{\{[^}]+\}\}/g, // {{var}} + /\[\[[^\]]+\]\]/g, // [[var]] + /\[[A-Z][A-Z0-9_\s]*\]/g, // [VAR] + ]; + + let count = 0; + for (const pattern of patterns) { + const matches = text.match(pattern); + if (matches) count += matches.length; + } + + return count; +} + +/** + * Calculate quality score based on various factors + */ +function calculateScore( + stats: QualityResult['stats'], + issues: QualityIssue[] +): number { + let score = 1.0; + + // Deduct for errors + const errors = issues.filter(i => i.type === 'error').length; + const warnings = issues.filter(i => i.type === 'warning').length; + + score -= errors * 0.2; + score -= warnings * 0.05; + + // Bonus for good structure + if (stats.hasRole) score += 0.05; + if (stats.hasTask) score += 0.05; + if (stats.hasConstraints) score += 0.03; + if (stats.hasExamples) score += 0.05; + + // Penalty for being too short + if (stats.wordCount < OPTIMAL_MIN_WORDS) { + score -= 0.1 * (1 - stats.wordCount / OPTIMAL_MIN_WORDS); + } + + // Slight penalty for being very long + if (stats.wordCount > OPTIMAL_MAX_WORDS) { + score -= 0.05; + } + + // Bonus for having variables (indicates reusability) + if (stats.variableCount > 0) { + score += 0.05; + } + + return Math.max(0, Math.min(1, score)); +} + +/** + * Check prompt quality locally (no API needed) + */ +export function check(prompt: string): QualityResult { + const issues: QualityIssue[] = []; + const trimmed = prompt.trim(); + + // Basic stats + const characterCount = trimmed.length; + const words = trimmed.split(/\s+/).filter(w => w.length > 0); + const wordCount = words.length; + const sentenceCount = (trimmed.match(/[.!?]+/g) || []).length || 1; + const variableCount = countVariables(trimmed); + const patterns = detectPatterns(trimmed); + + // Check for empty or too short + if (characterCount === 0) { + issues.push({ + type: 'error', + code: 'EMPTY', + message: 'Prompt is empty', + }); + } else if (characterCount < MIN_CHAR_COUNT) { + issues.push({ + type: 'error', + code: 'TOO_SHORT', + message: `Prompt is too short (${characterCount} chars, minimum ${MIN_CHAR_COUNT})`, + }); + } + + if (wordCount > 0 && wordCount < MIN_WORD_COUNT) { + issues.push({ + type: 'warning', + code: 'FEW_WORDS', + message: `Prompt has very few words (${wordCount} words, recommended ${OPTIMAL_MIN_WORDS}+)`, + }); + } + + // Check for gibberish + if (isGibberish(trimmed)) { + issues.push({ + type: 'error', + code: 'GIBBERISH', + message: 'Prompt appears to contain gibberish or random characters', + }); + } + + // Check for common issues + if (!patterns.hasTask && !patterns.hasRole) { + issues.push({ + type: 'suggestion', + code: 'NO_CLEAR_INSTRUCTION', + message: 'Consider adding a clear task or role definition', + }); + } + + // Check for unbalanced brackets/quotes + const brackets = [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + ]; + + for (const { open, close } of brackets) { + const openCount = (trimmed.match(new RegExp(`\\${open}`, 'g')) || []).length; + const closeCount = (trimmed.match(new RegExp(`\\${close}`, 'g')) || []).length; + + if (openCount !== closeCount) { + issues.push({ + type: 'warning', + code: 'UNBALANCED_BRACKETS', + message: `Unbalanced ${open}${close} brackets (${openCount} open, ${closeCount} close)`, + }); + } + } + + // Check for very long lines (readability) + const lines = trimmed.split('\n'); + const longLines = lines.filter(l => l.length > 500); + if (longLines.length > 0) { + issues.push({ + type: 'suggestion', + code: 'LONG_LINES', + message: 'Some lines are very long. Consider breaking them up for readability.', + }); + } + + const stats = { + characterCount, + wordCount, + sentenceCount, + variableCount, + ...patterns, + }; + + const score = calculateScore(stats, issues); + const hasErrors = issues.some(i => i.type === 'error'); + + return { + valid: !hasErrors, + score, + issues, + stats, + }; +} + +/** + * Validate a prompt and throw if invalid + */ +export function validate(prompt: string): void { + const result = check(prompt); + + if (!result.valid) { + const errors = result.issues + .filter(i => i.type === 'error') + .map(i => i.message) + .join('; '); + + throw new Error(`Invalid prompt: ${errors}`); + } +} + +/** + * Check if a prompt is valid + */ +export function isValid(prompt: string): boolean { + return check(prompt).valid; +} + +/** + * Get suggestions for improving a prompt + */ +export function getSuggestions(prompt: string): string[] { + const result = check(prompt); + const suggestions: string[] = []; + + // From issues + suggestions.push( + ...result.issues + .filter(i => i.type === 'suggestion' || i.type === 'warning') + .map(i => i.message) + ); + + // Additional suggestions based on stats + if (!result.stats.hasRole) { + suggestions.push('Add a role definition (e.g., "Act as a...")'); + } + + if (!result.stats.hasConstraints && result.stats.wordCount > 50) { + suggestions.push('Consider adding constraints or rules for better control'); + } + + if (!result.stats.hasExamples && result.stats.wordCount > 100) { + suggestions.push('Adding examples can improve output quality'); + } + + if (result.stats.variableCount === 0 && result.stats.wordCount > 30) { + suggestions.push('Consider adding variables (${var}) to make the prompt reusable'); + } + + return suggestions; +} diff --git a/packages/prompts.chat/src/similarity/index.ts b/packages/prompts.chat/src/similarity/index.ts new file mode 100644 index 00000000..505e44f0 --- /dev/null +++ b/packages/prompts.chat/src/similarity/index.ts @@ -0,0 +1,179 @@ +/** + * Content similarity utilities for duplicate detection + */ + +/** + * Normalize content for comparison by: + * - Removing variables (${...} patterns) + * - Converting to lowercase + * - Removing extra whitespace + * - Removing punctuation + */ +export function normalizeContent(content: string): string { + return content + // Remove variables like ${variable} or ${variable:default} + .replace(/\$\{[^}]+\}/g, "") + // Remove common placeholder patterns like [placeholder] or + .replace(/\[[^\]]+\]/g, "") + .replace(/<[^>]+>/g, "") + // Convert to lowercase + .toLowerCase() + // Remove punctuation + .replace(/[^\w\s]/g, "") + // Normalize whitespace + .replace(/\s+/g, " ") + .trim(); +} + +/** + * Calculate Jaccard similarity between two strings + * Returns a value between 0 (completely different) and 1 (identical) + */ +function jaccardSimilarity(str1: string, str2: string): number { + const set1 = new Set(str1.split(" ").filter(Boolean)); + const set2 = new Set(str2.split(" ").filter(Boolean)); + + if (set1.size === 0 && set2.size === 0) return 1; + if (set1.size === 0 || set2.size === 0) return 0; + + const intersection = new Set([...set1].filter(x => set2.has(x))); + const union = new Set([...set1, ...set2]); + + return intersection.size / union.size; +} + +/** + * Calculate n-gram similarity for better sequence matching + * Uses trigrams (3-character sequences) by default + */ +function ngramSimilarity(str1: string, str2: string, n: number = 3): number { + const getNgrams = (str: string): Set => { + const ngrams = new Set(); + const padded = " ".repeat(n - 1) + str + " ".repeat(n - 1); + for (let i = 0; i <= padded.length - n; i++) { + ngrams.add(padded.slice(i, i + n)); + } + return ngrams; + }; + + const ngrams1 = getNgrams(str1); + const ngrams2 = getNgrams(str2); + + if (ngrams1.size === 0 && ngrams2.size === 0) return 1; + if (ngrams1.size === 0 || ngrams2.size === 0) return 0; + + const intersection = new Set([...ngrams1].filter(x => ngrams2.has(x))); + const union = new Set([...ngrams1, ...ngrams2]); + + return intersection.size / union.size; +} + +/** + * Combined similarity score using multiple algorithms + * Returns a value between 0 (completely different) and 1 (identical) + */ +export function calculateSimilarity(content1: string, content2: string): number { + const normalized1 = normalizeContent(content1); + const normalized2 = normalizeContent(content2); + + // Exact match after normalization + if (normalized1 === normalized2) return 1; + + // Empty content edge case + if (!normalized1 || !normalized2) return 0; + + // Combine Jaccard (word-level) and n-gram (character-level) similarities + const jaccard = jaccardSimilarity(normalized1, normalized2); + const ngram = ngramSimilarity(normalized1, normalized2); + + // Weighted average: 60% Jaccard (word overlap), 40% n-gram (sequence similarity) + return jaccard * 0.6 + ngram * 0.4; +} + +/** + * Alias for calculateSimilarity + */ +export const calculate = calculateSimilarity; + +/** + * Check if two contents are similar enough to be considered duplicates + * Default threshold is 0.85 (85% similar) + */ +export function isSimilarContent( + content1: string, + content2: string, + threshold: number = 0.85 +): boolean { + return calculateSimilarity(content1, content2) >= threshold; +} + +/** + * Alias for isSimilarContent + */ +export const isDuplicate = isSimilarContent; + +/** + * Get normalized content hash for database indexing/comparison + * This is a simple hash for quick lookups before full similarity check + */ +export function getContentFingerprint(content: string): string { + const normalized = normalizeContent(content); + // Take first 500 chars of normalized content as fingerprint + return normalized.slice(0, 500); +} + +/** + * Find duplicates in an array of prompts + * Returns groups of similar prompts + */ +export function findDuplicates( + prompts: T[], + threshold: number = 0.85 +): T[][] { + const groups: T[][] = []; + const used = new Set(); + + for (let i = 0; i < prompts.length; i++) { + if (used.has(i)) continue; + + const group: T[] = [prompts[i]]; + used.add(i); + + for (let j = i + 1; j < prompts.length; j++) { + if (used.has(j)) continue; + + if (isSimilarContent(prompts[i].content, prompts[j].content, threshold)) { + group.push(prompts[j]); + used.add(j); + } + } + + if (group.length > 1) { + groups.push(group); + } + } + + return groups; +} + +/** + * Deduplicate an array of prompts, keeping the first occurrence + */ +export function deduplicate( + prompts: T[], + threshold: number = 0.85 +): T[] { + const result: T[] = []; + + for (const prompt of prompts) { + const isDupe = result.some(existing => + isSimilarContent(existing.content, prompt.content, threshold) + ); + + if (!isDupe) { + result.push(prompt); + } + } + + return result; +} diff --git a/packages/prompts.chat/src/variables/index.ts b/packages/prompts.chat/src/variables/index.ts new file mode 100644 index 00000000..c84a3d94 --- /dev/null +++ b/packages/prompts.chat/src/variables/index.ts @@ -0,0 +1,297 @@ +/** + * Variable Detection Utility + * Detects common variable-like patterns in text that could be converted + * to our supported format: ${variableName} or ${variableName:default} + */ + +export interface DetectedVariable { + original: string; + name: string; + defaultValue?: string; + pattern: VariablePattern; + startIndex: number; + endIndex: number; +} + +export type VariablePattern = + | "double_bracket" // [[name]] or [[ name ]] + | "double_curly" // {{name}} or {{ name }} + | "single_bracket" // [NAME] or [name] + | "single_curly" // {NAME} or {name} + | "angle_bracket" // or + | "percent" // %NAME% or %name% + | "dollar_curly"; // ${name} (already our format) + +interface PatternConfig { + pattern: VariablePattern; + regex: RegExp; + extractName: (match: RegExpExecArray) => string; + extractDefault?: (match: RegExpExecArray) => string | undefined; +} + +// Patterns to detect, ordered by specificity (more specific first) +const PATTERNS: PatternConfig[] = [ + // Double bracket: [[name]] or [[ name ]] or [[name: default]] + { + pattern: "double_bracket", + regex: /\[\[\s*([a-zA-Z_][a-zA-Z0-9_\s]*?)(?:\s*:\s*([^\]]*?))?\s*\]\]/g, + extractName: (m) => m[1].trim(), + extractDefault: (m) => m[2]?.trim(), + }, + // Double curly: {{name}} or {{ name }} or {{name: default}} + { + pattern: "double_curly", + regex: /\{\{\s*([a-zA-Z_][a-zA-Z0-9_\s]*?)(?:\s*:\s*([^}]*?))?\s*\}\}/g, + extractName: (m) => m[1].trim(), + extractDefault: (m) => m[2]?.trim(), + }, + // Our supported format (to exclude from warnings) + { + pattern: "dollar_curly", + regex: /\$\{([a-zA-Z_][a-zA-Z0-9_\s]*?)(?::([^}]*))?\}/g, + extractName: (m) => m[1].trim(), + }, + // Single bracket with uppercase or placeholder-like: [NAME] or [Your Name] + { + pattern: "single_bracket", + regex: /\[([A-Z][A-Z0-9_\s]*|[A-Za-z][a-zA-Z0-9_]*(?:\s+[A-Za-z][a-zA-Z0-9_]*)*)\]/g, + extractName: (m) => m[1].trim(), + }, + // Single curly with uppercase: {NAME} or {Your Name} + { + pattern: "single_curly", + regex: /\{([A-Z][A-Z0-9_\s]*|[A-Za-z][a-zA-Z0-9_]*(?:\s+[A-Za-z][a-zA-Z0-9_]*)*)\}/g, + extractName: (m) => m[1].trim(), + }, + // Angle brackets: or + { + pattern: "angle_bracket", + regex: /<([A-Z][A-Z0-9_\s]*|[a-zA-Z_][a-zA-Z0-9_\s]*)>/g, + extractName: (m) => m[1].trim(), + }, + // Percent signs: %NAME% or %name% + { + pattern: "percent", + regex: /%([a-zA-Z_][a-zA-Z0-9_]*)%/g, + extractName: (m) => m[1].trim(), + }, +]; + +// Common false positives to ignore +const FALSE_POSITIVES = new Set([ + // HTML/XML common tags + "div", "span", "p", "a", "br", "hr", "img", "input", "button", + "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "table", + "tr", "td", "th", "thead", "tbody", "form", "label", "select", + "option", "textarea", "script", "style", "link", "meta", "head", + "body", "html", "section", "article", "nav", "header", "footer", + "main", "aside", "figure", "figcaption", "strong", "em", "code", + "pre", "blockquote", "cite", "abbr", "address", "b", "i", "u", + // Common programming constructs + "if", "else", "for", "while", "switch", "case", "break", "return", + "function", "class", "const", "let", "var", "import", "export", + "default", "try", "catch", "finally", "throw", "new", "this", + "null", "undefined", "true", "false", "typeof", "instanceof", + // JSON structure keywords (when in context) + "type", "id", "key", "value", "data", "items", "properties", +]); + +/** + * Check if we're inside a JSON string context + */ +function isInsideJsonString(text: string, index: number): boolean { + let inString = false; + for (let i = 0; i < index; i++) { + if (text[i] === '"' && (i === 0 || text[i - 1] !== '\\')) { + inString = !inString; + } + } + return inString; +} + +/** + * Detect variable-like patterns in text + * Returns detected variables that are NOT in our supported format + */ +export function detectVariables(text: string): DetectedVariable[] { + const detected: DetectedVariable[] = []; + const seenRanges: Array<[number, number]> = []; + + // Track our supported format positions to exclude them + const supportedVars = new Set(); + const dollarCurlyPattern = /\$\{([a-zA-Z_][a-zA-Z0-9_\s]*?)(?::([^}]*))?\}/g; + let match: RegExpExecArray | null; + + while ((match = dollarCurlyPattern.exec(text)) !== null) { + seenRanges.push([match.index, match.index + match[0].length]); + supportedVars.add(match[0]); + } + + // Check each pattern + for (const config of PATTERNS) { + // Skip our supported format pattern for detection + if (config.pattern === "dollar_curly") continue; + + const regex = new RegExp(config.regex.source, config.regex.flags); + + while ((match = regex.exec(text)) !== null) { + const startIndex = match.index; + const endIndex = startIndex + match[0].length; + + // Check if this range overlaps with any already detected range + const overlaps = seenRanges.some( + ([start, end]) => + (startIndex >= start && startIndex < end) || + (endIndex > start && endIndex <= end) + ); + + if (overlaps) continue; + + const name = config.extractName(match); + + // Skip false positives + if (FALSE_POSITIVES.has(name.toLowerCase())) continue; + + // Skip very short names (likely not variables) + if (name.length < 2) continue; + + // For angle brackets, be more strict + if (config.pattern === "angle_bracket") { + if (!/^[A-Z]/.test(name) && !name.includes(" ")) continue; + } + + // For single curly/bracket in JSON context, be more careful + if ( + (config.pattern === "single_curly" || config.pattern === "single_bracket") && + isInsideJsonString(text, startIndex) + ) { + if (!/^[A-Z]/.test(name) && !name.includes(" ")) continue; + } + + const defaultValue = config.extractDefault?.(match); + + detected.push({ + original: match[0], + name, + defaultValue, + pattern: config.pattern, + startIndex, + endIndex, + }); + + seenRanges.push([startIndex, endIndex]); + } + } + + // Sort by position and remove duplicates + return detected + .sort((a, b) => a.startIndex - b.startIndex) + .filter((v, i, arr) => + i === 0 || v.original !== arr[i - 1].original || v.startIndex !== arr[i - 1].startIndex + ); +} + +/** + * Convert a detected variable to our supported format + */ +export function convertToSupportedFormat(variable: DetectedVariable): string { + // Normalize name: lowercase, replace spaces with underscores + const normalizedName = variable.name + .toLowerCase() + .replace(/\s+/g, "_") + .replace(/[^a-z0-9_]/g, ""); + + if (variable.defaultValue) { + return `\${${normalizedName}:${variable.defaultValue}}`; + } + + return `\${${normalizedName}}`; +} + +/** + * Convert all detected variables in text to our supported format + */ +export function convertAllVariables(text: string): string { + const detected = detectVariables(text); + + if (detected.length === 0) return text; + + // Sort by position descending to replace from end to start + const sorted = [...detected].sort((a, b) => b.startIndex - a.startIndex); + + let result = text; + for (const variable of sorted) { + const converted = convertToSupportedFormat(variable); + result = result.slice(0, variable.startIndex) + converted + result.slice(variable.endIndex); + } + + return result; +} + +/** + * Alias for convertAllVariables - normalizes all variable formats to ${var} + */ +export const normalize = convertAllVariables; + +/** + * Alias for detectVariables + */ +export const detect = detectVariables; + +/** + * Get a human-readable pattern description + */ +export function getPatternDescription(pattern: VariablePattern): string { + switch (pattern) { + case "double_bracket": return "[[...]]"; + case "double_curly": return "{{...}}"; + case "single_bracket": return "[...]"; + case "single_curly": return "{...}"; + case "angle_bracket": return "<...>"; + case "percent": return "%...%"; + case "dollar_curly": return "${...}"; + } +} + +/** + * Extract variables from our supported ${var} or ${var:default} format + */ +export function extractVariables(text: string): Array<{ name: string; defaultValue?: string }> { + const regex = /\$\{([a-zA-Z_][a-zA-Z0-9_\s]*?)(?::([^}]*))?\}/g; + const variables: Array<{ name: string; defaultValue?: string }> = []; + let match: RegExpExecArray | null; + + while ((match = regex.exec(text)) !== null) { + variables.push({ + name: match[1].trim(), + defaultValue: match[2]?.trim(), + }); + } + + return variables; +} + +/** + * Compile a prompt template with variable values + */ +export function compile( + template: string, + values: Record, + options: { useDefaults?: boolean } = {} +): string { + const { useDefaults = true } = options; + + return template.replace( + /\$\{([a-zA-Z_][a-zA-Z0-9_\s]*?)(?::([^}]*))?\}/g, + (match, name, defaultValue) => { + const trimmedName = name.trim(); + if (trimmedName in values) { + return values[trimmedName]; + } + if (useDefaults && defaultValue !== undefined) { + return defaultValue.trim(); + } + return match; // Keep original if no value and no default + } + ); +} diff --git a/packages/prompts.chat/tsconfig.json b/packages/prompts.chat/tsconfig.json new file mode 100644 index 00000000..1afef59d --- /dev/null +++ b/packages/prompts.chat/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "declaration": true, + "declarationMap": true, + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/prompts.chat/tsup.config.ts b/packages/prompts.chat/tsup.config.ts new file mode 100644 index 00000000..52eb6770 --- /dev/null +++ b/packages/prompts.chat/tsup.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: { + index: 'src/index.ts', + 'variables/index': 'src/variables/index.ts', + 'similarity/index': 'src/similarity/index.ts', + 'quality/index': 'src/quality/index.ts', + 'builder/index': 'src/builder/index.ts', + 'parser/index': 'src/parser/index.ts', + }, + format: ['cjs', 'esm'], + dts: true, + clean: true, + splitting: false, + sourcemap: true, +}); diff --git a/src/app/typed-prompts-editor/page.tsx b/src/app/typed-prompts-editor/page.tsx new file mode 100644 index 00000000..aa7099a3 --- /dev/null +++ b/src/app/typed-prompts-editor/page.tsx @@ -0,0 +1,5 @@ +import { PromptIde } from "@/components/ide/prompt-ide"; + +export default function IdePage() { + return ; +} diff --git a/src/components/ide/prompt-ide.tsx b/src/components/ide/prompt-ide.tsx new file mode 100644 index 00000000..35f2aee7 --- /dev/null +++ b/src/components/ide/prompt-ide.tsx @@ -0,0 +1,1692 @@ +"use client"; + +import { useState, useCallback, useEffect } from "react"; +import { useTranslations } from "next-intl"; +import Editor from "@monaco-editor/react"; +import { useTheme } from "next-themes"; +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Button } from "@/components/ui/button"; +import { Copy, Play, RotateCcw, Code2, FileJson, FileText, Book, ChevronDown, ChevronRight, Video, Music, Image, X, MessageSquare } from "lucide-react"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { toast } from "sonner"; +import { cn } from "@/lib/utils"; + +// Import the actual prompts.chat library +import { + builder, + fromPrompt, + templates, + video, + audio, + image, + chat, + chatPresets, + variables, + similarity, + quality, + parser, +} from "prompts.chat"; + +type OutputFormat = "json" | "yaml" | "markdown"; + +interface ApiSection { + name: string; + items: ApiItem[]; +} + +interface ApiItem { + name: string; + type: "function" | "class" | "type" | "interface" | "const"; + signature?: string; + description?: string; + example?: string; + returns?: string; + params?: { name: string; type: string; description: string }[]; +} + +const EXAMPLE_VIDEO = `import { video } from 'prompts.chat'; + +// Create a cinematic video prompt for Sora/Runway +const prompt = video() + // Scene - using individual methods for clarity + .scene("A lone samurai walks through an ancient bamboo forest") + .setting("Feudal Japan, remote mountain region") + .timeOfDay("golden-hour") + .weather("misty") + + // Subject - using object form + .subject({ + main: "A weathered samurai in traditional armor", + appearance: "battle-scarred face, grey streaks in hair tied in topknot", + clothing: "worn black and red yoroi armor, tattered cape flowing behind", + age: "50s", + count: "single" + }) + + // Camera - individual chainable methods + .shot("medium-wide") + .angle("low-angle") + .lens("anamorphic") + .focalLength("35mm") + .movement("tracking") + .cameraSpeed("slow") + .movementDirection("arc-left") + .aperture("f/1.4") + .filmStock("kodak-vision3-500t") + .filmGrain("subtle") + + // Lighting + .lighting({ + type: ["rim", "natural"], + time: "golden-hour", + direction: "back", + intensity: "dramatic" + }) + + // Actions - timed beats in the video + .action("Samurai pauses, hand resting on katana") + .action("Wind picks up, cape billows dramatically") + .action("He turns slowly to face camera") + + // Visual style + .look(["cinematic", "noir"]) + .era("1960s Japanese cinema") + .reference(["Akira Kurosawa", "Roger Deakins"]) + + // Color grading + .palette(["earthy", "muted"]) + .colorAnchors(["deep greens", "warm amber", "shadow blue"]) + .colorGrade("teal and orange with crushed blacks") + + // Audio elements + .ambient("wind rustling through bamboo, distant bird calls") + .diegetic(["footsteps on fallen leaves", "armor clinking softly"]) + .music("solo shakuhachi flute, melancholic melody") + + // Technical specifications + .duration(8) + .resolution("4K") + .fps(24) + .aspectRatio("21:9") + + // Mood and pacing + .mood(["melancholic", "epic", "mysterious"]) + .pacing("slow") + + .build(); + +prompt; +`; + +const EXAMPLE_AUDIO = `import { audio } from 'prompts.chat'; + +// Create a detailed music prompt for Suno/Udio +const prompt = audio() + // Genre - string or object form + .genre("synthwave") + .subgenre("darksynth") + .fusion(["electronic", "ambient"]) + + // Mood - takes primary and spread secondary args + .mood("nostalgic", "mysterious", "hopeful") + .energy("building") + .emotion("bittersweet longing for a retro-futuristic past") + + // Tempo - number or object + .bpm(108) + .tempoMarking("moderato") + .tempoFeel("driving") + + // Vocals + .vocalStyle(["male", "breathy"]) + .language("english") + .lyricsTheme("memories of a neon-lit city that never was") + .delivery("intimate and reflective, slight reverb") + + // Instrumentation - individual methods + .leadInstrument(["synthesizer", "electric-guitar"]) + .rhythmSection(["drums", "bass"]) + .bassInstrument("bass") + .percussion(["drums"]) + .pads(["synthesizer", "strings"]) + .featuredInstrument("synthesizer") + + // Song structure - section by section + .section("intro", 8, "Atmospheric synth pad fade-in") + .section("verse", 16, "Driving beat with vocals") + .section("pre-chorus", 8, "Building tension") + .section("chorus", 16, "Full arrangement, soaring melody") + .section("verse", 16, "Second verse, added elements") + .section("chorus", 16, "Repeat chorus with variations") + .section("bridge", 8, "Breakdown, stripped back") + .section("drop", 16, "Final chorus with extra energy") + .section("outro", 8, "Fade out with reverb tail") + + // Production style + .productionStyle(["polished", "atmospheric", "layered"]) + .era("1980s") + .reference(["The Midnight", "Gunship", "FM-84"]) + .texture("lush and cinematic with analog warmth") + .effects(["heavy reverb on snare", "chorus on guitars", "tape saturation"]) + + // Technical specs + .key("Am") + .timeSignature("4/4") + .duration(210) + + // Style tags - individual or batch + .tag("retrowave") + .tag("outrun") + .tags(["80s nostalgia", "neon", "night drive"]) + + .build(); + +prompt; +`; + +const EXAMPLE_IMAGE = `import { image } from 'prompts.chat'; + +// Create a detailed image prompt for Midjourney/DALL-E/Stable Diffusion +const prompt = image() + // Main subject - string or object form + .subject("An ancient library hidden inside a giant hollow tree") + .subjectDetails([ + "thousands of leather-bound books on curved wooden shelves", + "spiral staircase winding up through the center", + "glowing fireflies floating between bookshelves", + "worn reading chairs with velvet cushions" + ]) + .pose("majestic interior view") + .expression("magical and inviting atmosphere") + + // Environment - string or object form + .environment("interior of a massive ancient oak tree") + .location("enchanted forest, hidden realm") + .atmosphere("mystical, warm and cozy") + .season("autumn") + .props([ + "scattered scrolls and quills on oak desks", + "mystical runes carved into bark walls", + "mushrooms glowing softly in corners" + ]) + + // Camera - individual chainable methods + .angle("low-angle") + .shot("wide") + .lens("wide-angle") + .focalLength("24mm") + .focus("deep") + .cameraBrand("hasselblad") + .sensor("medium-format") + .aperture("f/8") + + // Lighting - object or individual methods + .lightingType(["practical", "rim"]) + .timeOfDay("twilight") + .lightDirection("three-quarter") + .lightIntensity("soft") + + // Composition - object or individual methods + .goldenRatio() + .foreground("scattered books and scrolls") + .midground("spiral staircase with warm glow") + .background("small windows showing starlit forest") + + // Art style + .medium("digital-art") + .artist(["Greg Rutkowski", "Thomas Kinkade", "Studio Ghibli"]) + .influence(["Art Nouveau", "cottage core aesthetic"]) + + // Color palette + .palette(["warm", "earthy"]) + .primaryColors(["amber", "deep brown", "forest green"]) + .accentColors(["soft gold", "moonlight blue", "fairy pink"]) + .colorGrade("rich saturation with deep shadows") + + // Mood + .mood(["serene", "peaceful", "nostalgic", "whimsical"]) + + // Technical specifications + .aspectRatio("3:2") + .resolution("8K") + .quality("masterpiece") + + // Negative prompt - takes array + .negative([ + "blurry", "low quality", "bad anatomy", "watermark", + "text", "signature", "modern elements", "plastic", + "harsh lighting", "overexposed", "underexposed" + ]) + + // Custom additions + .custom("volumetric lighting, ray tracing, octane render") + + .build(); + +prompt; +`; + +const EXAMPLE_CHAT = `import { chat } from 'prompts.chat'; + +// Create a chat prompt for conversational AI +const prompt = chat() + // Define the AI's role and expertise + .role("senior software architect") + .tone("professional") + .expertise("TypeScript", "React", "Node.js", "System Design") + + // Set the main task + .task("Review code and provide architectural feedback") + + // Configure reasoning style + .stepByStep() + + // Output format + .json() + + // Response length + .detailed() + + .build(); + +prompt; +`; + +const API_DOCS: ApiSection[] = [ + // ============================================================================ + // TEXT PROMPTS + // ============================================================================ + { + name: "Text Prompts", + items: [ + { name: "builder()", type: "function", signature: "builder(): PromptBuilder", description: "Create a new prompt builder" }, + { name: "fromPrompt()", type: "function", signature: "fromPrompt(content: string): PromptBuilder", description: "Create builder from existing prompt" }, + { name: ".role()", type: "function", signature: "role(role: string): this", description: "Set AI persona/role" }, + { name: ".context()", type: "function", signature: "context(ctx: string): this", description: "Set background context" }, + { name: ".task()", type: "function", signature: "task(task: string): this", description: "Set main instruction" }, + { name: ".constraints()", type: "function", signature: "constraints(c: string[]): this", description: "Add rules/constraints" }, + { name: ".output()", type: "function", signature: "output(format: string): this", description: "Set output format" }, + { name: ".example()", type: "function", signature: "example(in: string, out: string): this", description: "Add input/output example" }, + { name: ".variable()", type: "function", signature: "variable(name: string, opts?): this", description: "Define a placeholder variable" }, + { name: ".section()", type: "function", signature: "section(title: string, content: string): this", description: "Add custom section" }, + { name: ".build()", type: "function", signature: "build(): BuiltPrompt", description: "Build the final prompt" }, + ], + }, + { + name: "Templates", + items: [ + { name: "templates.codeReview()", type: "function", signature: "codeReview(opts?): PromptBuilder", description: "Code review template" }, + { name: "templates.translation()", type: "function", signature: "translation(from, to): PromptBuilder", description: "Translation template" }, + { name: "templates.summarize()", type: "function", signature: "summarize(opts?): PromptBuilder", description: "Summarization template" }, + { name: "templates.qa()", type: "function", signature: "qa(context?): PromptBuilder", description: "Q&A template" }, + ], + }, + // ============================================================================ + // CHAT PROMPTS + // ============================================================================ + { + name: "Chat: Messages", + items: [ + { name: "chat()", type: "function", signature: "chat(): ChatPromptBuilder", description: "Create chat prompt builder" }, + { name: ".system()", type: "function", signature: "system(content: string): this", description: "Set system message" }, + { name: ".user()", type: "function", signature: "user(content, name?): this", description: "Add user message" }, + { name: ".assistant()", type: "function", signature: "assistant(content: string): this", description: "Add assistant message" }, + { name: ".messages()", type: "function", signature: "messages(msgs[]): this", description: "Add multiple messages" }, + { name: ".conversation()", type: "function", signature: "conversation(turns[]): this", description: "Add conversation turns" }, + ], + }, + { + name: "Chat: Persona", + items: [ + { name: ".persona()", type: "function", signature: "persona(settings | string): this", description: "Set full persona" }, + { name: ".role()", type: "function", signature: "role(role: string): this", description: "Set AI role" }, + { name: ".tone()", type: "function", signature: "tone(tone): this", description: "Set tone(s)" }, + { name: ".expertise()", type: "function", signature: "expertise(areas): this", description: "Set expertise areas" }, + { name: ".personality()", type: "function", signature: "personality(traits[]): this", description: "Set personality traits" }, + { name: ".background()", type: "function", signature: "background(bg: string): this", description: "Set background info" }, + { name: ".speakAs()", type: "function", signature: "speakAs(name: string): this", description: "Set persona name" }, + ], + }, + { + name: "Chat: Context", + items: [ + { name: ".context()", type: "function", signature: "context(settings | string): this", description: "Set full context" }, + { name: ".domain()", type: "function", signature: "domain(domain: string): this", description: "Set knowledge domain" }, + { name: ".audience()", type: "function", signature: "audience(audience: string): this", description: "Set target audience" }, + { name: ".constraints()", type: "function", signature: "constraints(c[]): this", description: "Add constraints" }, + { name: ".knowledge()", type: "function", signature: "knowledge(facts[]): this", description: "Set known facts" }, + ], + }, + { + name: "Chat: Task", + items: [ + { name: ".task()", type: "function", signature: "task(instruction | ChatTask): this", description: "Set task" }, + { name: ".steps()", type: "function", signature: "steps(steps[]): this", description: "Set task steps" }, + { name: ".deliverables()", type: "function", signature: "deliverables(d[]): this", description: "Set deliverables" }, + { name: ".criteria()", type: "function", signature: "criteria(criteria[]): this", description: "Set success criteria" }, + { name: ".avoid()", type: "function", signature: "avoid(antiPatterns[]): this", description: "Set anti-patterns" }, + ], + }, + { + name: "Chat: Output", + items: [ + { name: ".json()", type: "function", signature: "json(schema?): this", description: "JSON output" }, + { name: ".jsonSchema()", type: "function", signature: "jsonSchema(name, schema): this", description: "JSON with schema" }, + { name: ".markdown()", type: "function", signature: "markdown(): this", description: "Markdown output" }, + { name: ".code()", type: "function", signature: "code(language?): this", description: "Code output" }, + { name: ".brief()", type: "function", signature: "brief(): this", description: "Brief output" }, + { name: ".detailed()", type: "function", signature: "detailed(): this", description: "Detailed output" }, + { name: ".comprehensive()", type: "function", signature: "comprehensive(): this", description: "Comprehensive output" }, + { name: ".withExamples()", type: "function", signature: "withExamples(): this", description: "Include examples" }, + { name: ".withSources()", type: "function", signature: "withSources(): this", description: "Cite sources" }, + ], + }, + { + name: "Chat: Reasoning", + items: [ + { name: ".stepByStep()", type: "function", signature: "stepByStep(): this", description: "Step-by-step reasoning" }, + { name: ".chainOfThought()", type: "function", signature: "chainOfThought(): this", description: "Chain-of-thought" }, + { name: ".treeOfThought()", type: "function", signature: "treeOfThought(): this", description: "Tree-of-thought" }, + { name: ".firstPrinciples()", type: "function", signature: "firstPrinciples(): this", description: "First principles" }, + { name: ".devilsAdvocate()", type: "function", signature: "devilsAdvocate(): this", description: "Devil's advocate" }, + { name: ".showWork()", type: "function", signature: "showWork(): this", description: "Show reasoning" }, + { name: ".verifyAnswer()", type: "function", signature: "verifyAnswer(): this", description: "Verify answer" }, + ], + }, + { + name: "Chat: Presets", + items: [ + { name: "chatPresets.coder()", type: "function", signature: "coder(lang?): ChatPromptBuilder", description: "Expert coder" }, + { name: "chatPresets.writer()", type: "function", signature: "writer(style?): ChatPromptBuilder", description: "Writer assistant" }, + { name: "chatPresets.tutor()", type: "function", signature: "tutor(subject?): ChatPromptBuilder", description: "Patient tutor" }, + { name: "chatPresets.analyst()", type: "function", signature: "analyst(): ChatPromptBuilder", description: "Data analyst" }, + { name: "chatPresets.socratic()", type: "function", signature: "socratic(): ChatPromptBuilder", description: "Socratic teacher" }, + { name: "chatPresets.summarizer()", type: "function", signature: "summarizer(len?): ChatPromptBuilder", description: "Summarizer" }, + { name: "chatPresets.translator()", type: "function", signature: "translator(lang): ChatPromptBuilder", description: "Translator" }, + ], + }, + // ============================================================================ + // MEDIA: IMAGE + // ============================================================================ + { + name: "Image: Subject", + items: [ + { name: "image()", type: "function", signature: "image(): ImagePromptBuilder", description: "Create image prompt builder" }, + { name: ".subject()", type: "function", signature: "subject(main | ImageSubject): this", description: "Set main subject" }, + { name: ".subjectDetails()", type: "function", signature: "subjectDetails(details[]): this", description: "Add subject details" }, + { name: ".expression()", type: "function", signature: "expression(expr: string): this", description: "Set facial expression" }, + { name: ".pose()", type: "function", signature: "pose(pose: string): this", description: "Set body pose" }, + { name: ".clothing()", type: "function", signature: "clothing(clothing: string): this", description: "Set clothing" }, + ], + }, + { + name: "Image: Environment", + items: [ + { name: ".environment()", type: "function", signature: "environment(setting | ImageEnv): this", description: "Set environment" }, + { name: ".location()", type: "function", signature: "location(location: string): this", description: "Set location" }, + { name: ".props()", type: "function", signature: "props(props[]): this", description: "Set scene props" }, + { name: ".atmosphere()", type: "function", signature: "atmosphere(atm: string): this", description: "Set atmosphere" }, + { name: ".season()", type: "function", signature: "season(season): this", description: "Set season" }, + ], + }, + { + name: "Image: Camera", + items: [ + { name: ".camera()", type: "function", signature: "camera(settings): this", description: "Set all camera settings" }, + { name: ".shot()", type: "function", signature: "shot(shot: ShotType): this", description: "Set shot type" }, + { name: ".angle()", type: "function", signature: "angle(angle: CameraAngle): this", description: "Set camera angle" }, + { name: ".lens()", type: "function", signature: "lens(lens: LensType): this", description: "Set lens type" }, + { name: ".focus()", type: "function", signature: "focus(focus: FocusType): this", description: "Set focus type" }, + { name: ".aperture()", type: "function", signature: "aperture(aperture: string): this", description: "Set aperture" }, + { name: ".bokeh()", type: "function", signature: "bokeh(style: BokehStyle): this", description: "Set bokeh style" }, + { name: ".filmStock()", type: "function", signature: "filmStock(stock: FilmStock): this", description: "Set film stock" }, + ], + }, + { + name: "Image: Lighting", + items: [ + { name: ".lighting()", type: "function", signature: "lighting(settings): this", description: "Set all lighting" }, + { name: ".lightingType()", type: "function", signature: "lightingType(type): this", description: "Set lighting type(s)" }, + { name: ".timeOfDay()", type: "function", signature: "timeOfDay(time: TimeOfDay): this", description: "Set time of day" }, + { name: ".weather()", type: "function", signature: "weather(weather): this", description: "Set weather" }, + { name: ".lightDirection()", type: "function", signature: "lightDirection(dir): this", description: "Set light direction" }, + ], + }, + { + name: "Image: Composition", + items: [ + { name: ".composition()", type: "function", signature: "composition(settings): this", description: "Set all composition" }, + { name: ".ruleOfThirds()", type: "function", signature: "ruleOfThirds(): this", description: "Enable rule of thirds" }, + { name: ".goldenRatio()", type: "function", signature: "goldenRatio(): this", description: "Enable golden ratio" }, + { name: ".foreground()", type: "function", signature: "foreground(fg: string): this", description: "Set foreground" }, + { name: ".background()", type: "function", signature: "background(bg: string): this", description: "Set background" }, + ], + }, + { + name: "Image: Style", + items: [ + { name: ".style()", type: "function", signature: "style(settings): this", description: "Set all style" }, + { name: ".medium()", type: "function", signature: "medium(medium: ArtStyle): this", description: "Set art medium" }, + { name: ".artist()", type: "function", signature: "artist(artist): this", description: "Reference artist(s)" }, + { name: ".influence()", type: "function", signature: "influence(influences[]): this", description: "Set style influences" }, + { name: ".palette()", type: "function", signature: "palette(palette): this", description: "Set color palette" }, + { name: ".primaryColors()", type: "function", signature: "primaryColors(colors[]): this", description: "Set primary colors" }, + { name: ".colorGrade()", type: "function", signature: "colorGrade(grade: string): this", description: "Set color grade" }, + { name: ".mood()", type: "function", signature: "mood(mood): this", description: "Set mood(s)" }, + { name: ".negative()", type: "function", signature: "negative(items[]): this", description: "Negative prompts" }, + { name: ".build()", type: "function", signature: "build(): BuiltImagePrompt", description: "Build the prompt" }, + ], + }, + // ============================================================================ + // MEDIA: VIDEO + // ============================================================================ + { + name: "Video: Scene", + items: [ + { name: "video()", type: "function", signature: "video(): VideoPromptBuilder", description: "Create video prompt builder" }, + { name: ".scene()", type: "function", signature: "scene(desc | VideoScene): this", description: "Set scene description" }, + { name: ".setting()", type: "function", signature: "setting(setting: string): this", description: "Set location/setting" }, + { name: ".subject()", type: "function", signature: "subject(main | VideoSubject): this", description: "Set main subject" }, + { name: ".appearance()", type: "function", signature: "appearance(app: string): this", description: "Set appearance" }, + { name: ".clothing()", type: "function", signature: "clothing(clothing: string): this", description: "Set clothing" }, + ], + }, + { + name: "Video: Camera", + items: [ + { name: ".camera()", type: "function", signature: "camera(settings): this", description: "Set all camera settings" }, + { name: ".shot()", type: "function", signature: "shot(shot: ShotType): this", description: "Set shot type" }, + { name: ".angle()", type: "function", signature: "angle(angle: CameraAngle): this", description: "Set camera angle" }, + { name: ".movement()", type: "function", signature: "movement(movement): this", description: "Set camera movement" }, + { name: ".lens()", type: "function", signature: "lens(lens: LensType): this", description: "Set lens type" }, + { name: ".platform()", type: "function", signature: "platform(platform): this", description: "Set camera platform" }, + { name: ".cameraSpeed()", type: "function", signature: "cameraSpeed(speed): this", description: "Set movement speed" }, + { name: ".filmStock()", type: "function", signature: "filmStock(stock): this", description: "Set film stock" }, + { name: ".filmGrain()", type: "function", signature: "filmGrain(grain): this", description: "Set film grain" }, + ], + }, + { + name: "Video: Action", + items: [ + { name: ".action()", type: "function", signature: "action(action, opts?): this", description: "Add single action" }, + { name: ".actions()", type: "function", signature: "actions(actions[]): this", description: "Add multiple actions" }, + { name: ".motion()", type: "function", signature: "motion(settings): this", description: "Set motion settings" }, + { name: ".motionBeats()", type: "function", signature: "motionBeats(beats[]): this", description: "Set motion beats" }, + ], + }, + { + name: "Video: Style", + items: [ + { name: ".style()", type: "function", signature: "style(settings): this", description: "Set all style" }, + { name: ".look()", type: "function", signature: "look(look: ArtStyle): this", description: "Set visual look" }, + { name: ".era()", type: "function", signature: "era(era: string): this", description: "Set era/period" }, + { name: ".reference()", type: "function", signature: "reference(refs[]): this", description: "Set reference films" }, + { name: ".palette()", type: "function", signature: "palette(palette): this", description: "Set color palette" }, + { name: ".colorGrade()", type: "function", signature: "colorGrade(grade: string): this", description: "Set color grade" }, + { name: ".lighting()", type: "function", signature: "lighting(settings): this", description: "Set lighting" }, + { name: ".timeOfDay()", type: "function", signature: "timeOfDay(time): this", description: "Set time of day" }, + ], + }, + { + name: "Video: Audio", + items: [ + { name: ".audio()", type: "function", signature: "audio(settings): this", description: "Set all audio" }, + { name: ".dialogue()", type: "function", signature: "dialogue(dialogue: string): this", description: "Set dialogue" }, + { name: ".ambient()", type: "function", signature: "ambient(ambient: string): this", description: "Set ambient sound" }, + { name: ".diegetic()", type: "function", signature: "diegetic(sounds[]): this", description: "Set diegetic sounds" }, + { name: ".music()", type: "function", signature: "music(music: string): this", description: "Set music" }, + ], + }, + { + name: "Video: Technical", + items: [ + { name: ".duration()", type: "function", signature: "duration(seconds: number): this", description: "Set duration" }, + { name: ".resolution()", type: "function", signature: "resolution(res): this", description: "Set resolution" }, + { name: ".fps()", type: "function", signature: "fps(fps): this", description: "Set frame rate" }, + { name: ".aspectRatio()", type: "function", signature: "aspectRatio(ratio): this", description: "Set aspect ratio" }, + { name: ".mood()", type: "function", signature: "mood(mood): this", description: "Set mood(s)" }, + { name: ".pacing()", type: "function", signature: "pacing(pacing): this", description: "Set pacing" }, + { name: ".build()", type: "function", signature: "build(): BuiltVideoPrompt", description: "Build the prompt" }, + ], + }, + // ============================================================================ + // MEDIA: AUDIO + // ============================================================================ + { + name: "Audio: Genre", + items: [ + { name: "audio()", type: "function", signature: "audio(): AudioPromptBuilder", description: "Create audio prompt builder" }, + { name: ".genre()", type: "function", signature: "genre(primary: MusicGenre): this", description: "Set primary genre" }, + { name: ".subgenre()", type: "function", signature: "subgenre(sub: string): this", description: "Set subgenre" }, + { name: ".fusion()", type: "function", signature: "fusion(genres[]): this", description: "Blend multiple genres" }, + ], + }, + { + name: "Audio: Mood", + items: [ + { name: ".mood()", type: "function", signature: "mood(primary, ...more): this", description: "Set moods (variadic)" }, + { name: ".energy()", type: "function", signature: "energy(level): this", description: "Set energy level" }, + { name: ".emotion()", type: "function", signature: "emotion(emotion: string): this", description: "Set emotion" }, + ], + }, + { + name: "Audio: Tempo", + items: [ + { name: ".tempo()", type: "function", signature: "tempo(bpm | AudioTempo): this", description: "Set tempo" }, + { name: ".bpm()", type: "function", signature: "bpm(bpm: number): this", description: "Set BPM" }, + { name: ".tempoMarking()", type: "function", signature: "tempoMarking(marking): this", description: "Classical tempo" }, + { name: ".tempoFeel()", type: "function", signature: "tempoFeel(feel): this", description: "Set rhythmic feel" }, + ], + }, + { + name: "Audio: Vocals", + items: [ + { name: ".vocals()", type: "function", signature: "vocals(settings): this", description: "Set all vocal settings" }, + { name: ".vocalStyle()", type: "function", signature: "vocalStyle(style): this", description: "Set vocal style(s)" }, + { name: ".language()", type: "function", signature: "language(lang): this", description: "Set language" }, + { name: ".lyricsTheme()", type: "function", signature: "lyricsTheme(theme: string): this", description: "Set lyrics theme" }, + { name: ".delivery()", type: "function", signature: "delivery(delivery: string): this", description: "Set vocal delivery" }, + { name: ".instrumental()", type: "function", signature: "instrumental(): this", description: "Make instrumental" }, + ], + }, + { + name: "Audio: Instruments", + items: [ + { name: ".instruments()", type: "function", signature: "instruments(instruments[]): this", description: "Set instruments" }, + { name: ".leadInstrument()", type: "function", signature: "leadInstrument(inst): this", description: "Set lead instrument" }, + { name: ".rhythmSection()", type: "function", signature: "rhythmSection(instruments[]): this", description: "Set rhythm section" }, + { name: ".bassInstrument()", type: "function", signature: "bassInstrument(inst): this", description: "Set bass" }, + { name: ".percussion()", type: "function", signature: "percussion(instruments): this", description: "Set percussion" }, + { name: ".pads()", type: "function", signature: "pads(instruments): this", description: "Set pad sounds" }, + ], + }, + { + name: "Audio: Structure", + items: [ + { name: ".structure()", type: "function", signature: "structure(settings): this", description: "Set structure" }, + { name: ".section()", type: "function", signature: "section(type, bars?, desc?): this", description: "Add song section" }, + { name: ".duration()", type: "function", signature: "duration(seconds): this", description: "Set duration" }, + ], + }, + { + name: "Audio: Production", + items: [ + { name: ".production()", type: "function", signature: "production(settings): this", description: "Production settings" }, + { name: ".productionStyle()", type: "function", signature: "productionStyle(style): this", description: "Set production style" }, + { name: ".era()", type: "function", signature: "era(era: Era): this", description: "Set era/decade" }, + { name: ".reference()", type: "function", signature: "reference(artists[]): this", description: "Reference artists" }, + { name: ".texture()", type: "function", signature: "texture(texture: string): this", description: "Set sound texture" }, + { name: ".effects()", type: "function", signature: "effects(effects[]): this", description: "Add audio effects" }, + { name: ".key()", type: "function", signature: "key(key: MusicalKey): this", description: "Set musical key" }, + { name: ".tags()", type: "function", signature: "tags(tags[]): this", description: "Add style tags" }, + { name: ".build()", type: "function", signature: "build(): BuiltAudioPrompt", description: "Build the prompt" }, + ], + }, + // ============================================================================ + // TYPES + // ============================================================================ + { + name: "Chat Types", + items: [ + { name: "MessageRole", type: "type", description: "'system' | 'user' | 'assistant'" }, + { name: "PersonaTone", type: "type", description: "'professional' | 'casual' | 'technical' | 'friendly' | ..." }, + { name: "PersonaExpertise", type: "type", description: "'coding' | 'writing' | 'analysis' | 'teaching' | ..." }, + { name: "ReasoningStyle", type: "type", description: "'step-by-step' | 'chain-of-thought' | 'tree-of-thought' | ..." }, + { name: "OutputLength", type: "type", description: "'brief' | 'moderate' | 'detailed' | 'comprehensive'" }, + { name: "OutputStyle", type: "type", description: "'prose' | 'bullet-points' | 'numbered-list' | 'table' | ..." }, + ], + }, + { + name: "Camera Types", + items: [ + { name: "ShotType", type: "type", description: "'close-up' | 'medium' | 'wide' | 'extreme-wide' | 'portrait' | ..." }, + { name: "CameraAngle", type: "type", description: "'eye-level' | 'low-angle' | 'high-angle' | 'dutch-angle' | ..." }, + { name: "CameraMovement", type: "type", description: "'static' | 'pan' | 'tilt' | 'dolly' | 'tracking' | 'crane' | ..." }, + { name: "LensType", type: "type", description: "'wide-angle' | '35mm' | '50mm' | '85mm' | 'telephoto' | ..." }, + { name: "FocusType", type: "type", description: "'shallow' | 'deep' | 'soft-focus' | 'bokeh-heavy' | ..." }, + { name: "BokehStyle", type: "type", description: "'smooth' | 'creamy' | 'swirly' | 'soap-bubble' | ..." }, + ], + }, + { + name: "Style Types", + items: [ + { name: "LightingType", type: "type", description: "'natural' | 'studio' | 'dramatic' | 'rim' | 'rembrandt' | ..." }, + { name: "TimeOfDay", type: "type", description: "'dawn' | 'golden-hour' | 'midday' | 'blue-hour' | 'night' | ..." }, + { name: "ArtStyle", type: "type", description: "'photorealistic' | 'cinematic' | 'anime' | 'oil-painting' | ..." }, + { name: "ColorPalette", type: "type", description: "'warm' | 'cool' | 'vibrant' | 'muted' | 'monochrome' | ..." }, + { name: "Mood", type: "type", description: "'serene' | 'dramatic' | 'mysterious' | 'romantic' | 'epic' | ..." }, + { name: "FilmStock", type: "type", description: "'kodak-portra-400' | 'fujifilm-velvia-50' | 'cinestill-800t' | ..." }, + ], + }, + { + name: "Audio Types", + items: [ + { name: "MusicGenre", type: "type", description: "'pop' | 'rock' | 'jazz' | 'electronic' | 'synthwave' | ..." }, + { name: "Instrument", type: "type", description: "'piano' | 'guitar' | 'drums' | 'synthesizer' | '808' | ..." }, + { name: "VocalStyle", type: "type", description: "'male' | 'female' | 'choir' | 'rap' | 'falsetto' | ..." }, + { name: "TempoMarking", type: "type", description: "'largo' | 'adagio' | 'andante' | 'allegro' | 'presto'" }, + { name: "SongSection", type: "type", description: "'intro' | 'verse' | 'chorus' | 'bridge' | 'drop' | 'outro'" }, + { name: "ProductionStyle", type: "type", description: "'lo-fi' | 'vintage' | 'modern' | 'polished' | 'raw' | ..." }, + { name: "MusicalKey", type: "type", description: "'C' | 'Am' | 'G' | 'Em' | 'D' | 'Bm' | ..." }, + ], + }, + // ============================================================================ + // UTILITIES + // ============================================================================ + { + name: "Parser", + items: [ + { name: "parsePrompt", type: "function", signature: "parse(content, format?): ParsedPrompt", description: "Parse prompt from string (YAML/JSON/MD)" }, + { name: "toYaml", type: "function", signature: "toYaml(prompt): string", description: "Serialize to YAML format" }, + { name: "toJson", type: "function", signature: "toJson(prompt, pretty?): string", description: "Serialize to JSON format" }, + { name: "getSystemPrompt", type: "function", signature: "getSystemPrompt(prompt): string", description: "Extract system message content" }, + { name: "interpolate", type: "function", signature: "interpolate(prompt, values): ParsedPrompt", description: "Fill in {{var}} placeholders" }, + ], + }, + { + name: "Variables", + items: [ + { name: "detectVariables", type: "function", signature: "detectVariables(text): DetectedVariable[]", description: "Find all variable patterns in text" }, + { name: "normalizeVariables", type: "function", signature: "normalize(content): string", description: "Convert all to ${var} format" }, + { name: "extractVariables", type: "function", signature: "extractVariables(text): {name,default?}[]", description: "Extract ${var} variables" }, + { name: "compile", type: "function", signature: "compile(template, values, opts?): string", description: "Fill template with values" }, + { name: "convertToSupportedFormat", type: "function", signature: "convertToSupportedFormat(v): string", description: "Convert single variable" }, + { name: "convertAllVariables", type: "function", signature: "convertAllVariables(text): string", description: "Convert all variables in text" }, + ], + }, + { + name: "Quality", + items: [ + { name: "checkQuality", type: "function", signature: "check(prompt): QualityResult", description: "Check prompt quality (score 0-1)" }, + { name: "validatePrompt", type: "function", signature: "validate(prompt): void", description: "Throw if prompt is invalid" }, + { name: "isValidPrompt", type: "function", signature: "isValid(prompt): boolean", description: "Check if prompt is valid" }, + { name: "getSuggestions", type: "function", signature: "getSuggestions(prompt): string[]", description: "Get improvement suggestions" }, + ], + }, + { + name: "Similarity", + items: [ + { name: "calculateSimilarity", type: "function", signature: "calculate(a, b): number", description: "Get similarity score 0-1" }, + { name: "isSimilarContent", type: "function", signature: "isSimilar(a, b, threshold?): boolean", description: "Check if similar (default 0.85)" }, + { name: "normalizeContent", type: "function", signature: "normalizeContent(text): string", description: "Normalize text for comparison" }, + { name: "getContentFingerprint", type: "function", signature: "getContentFingerprint(text): string", description: "Get content hash for indexing" }, + { name: "findDuplicates", type: "function", signature: "findDuplicates(items, threshold?): T[][]", description: "Find groups of duplicates" }, + { name: "deduplicate", type: "function", signature: "deduplicate(items, threshold?): T[]", description: "Remove duplicates, keep first" }, + ], + }, +]; + +const DEFAULT_CODE = `import { builder, templates } from 'prompts.chat'; + +// Create a prompt using the fluent builder API +const prompt = builder() + .role("Senior TypeScript Developer") + .context("You are helping review code for a startup") + .task("Analyze the following code for bugs and improvements") + .constraints([ + "Be concise and actionable", + "Focus on critical issues first", + "Suggest modern TypeScript best practices" + ]) + .output("JSON with { bugs: [], suggestions: [], rating: number }") + .variable("code", { required: true, description: "The code to review" }) + .build(); + +// Or use pre-built templates +const translatePrompt = templates.translation("English", "Spanish").build(); + +// Export the main prompt +prompt; +`; + +const TYPE_DEFINITIONS = ` +declare module 'prompts.chat' { + // ============================================================================ + // BUILDER TYPES + // ============================================================================ + + export interface PromptVariable { + name: string; + description?: string; + required?: boolean; + defaultValue?: string; + } + + export interface BuiltPrompt { + content: string; + variables: PromptVariable[]; + metadata: { + role?: string; + context?: string; + task?: string; + constraints?: string[]; + outputFormat?: string; + examples?: Array<{ input: string; output: string }>; + }; + } + + export class PromptBuilder { + role(role: string): this; + persona(persona: string): this; + context(context: string): this; + background(background: string): this; + task(task: string): this; + instruction(instruction: string): this; + constraints(constraints: string[]): this; + constraint(constraint: string): this; + rules(rules: string[]): this; + output(format: string): this; + format(format: string): this; + example(input: string, output: string): this; + examples(examples: Array<{ input: string; output: string }>): this; + variable(name: string, options?: { description?: string; required?: boolean; defaultValue?: string }): this; + section(title: string, content: string): this; + raw(content: string): this; + build(): BuiltPrompt; + toString(): string; + } + + export function builder(): PromptBuilder; + export function fromPrompt(content: string): PromptBuilder; + + export const templates: { + codeReview(options?: { language?: string; focus?: string[] }): PromptBuilder; + translation(from: string, to: string): PromptBuilder; + summarize(options?: { maxLength?: number; style?: 'bullet' | 'paragraph' }): PromptBuilder; + qa(context?: string): PromptBuilder; + }; + + // ============================================================================ + // CHAT BUILDER TYPES + // ============================================================================ + + export type PersonaTone = 'professional' | 'casual' | 'formal' | 'friendly' | 'academic' | 'technical' | 'creative' | 'empathetic' | 'authoritative' | 'playful' | 'concise' | 'detailed' | 'socratic' | 'coaching' | 'analytical' | 'encouraging' | 'neutral' | 'humorous' | 'serious'; + export type PersonaExpertise = 'general' | 'coding' | 'writing' | 'analysis' | 'research' | 'teaching' | 'counseling' | 'creative' | 'legal' | 'medical' | 'financial' | 'scientific' | 'engineering' | 'design' | 'marketing' | 'business' | 'philosophy' | 'history' | 'languages' | 'mathematics'; + export type ReasoningStyle = 'step-by-step' | 'chain-of-thought' | 'tree-of-thought' | 'direct' | 'analytical' | 'comparative' | 'deductive' | 'inductive' | 'first-principles' | 'analogical' | 'devil-advocate'; + export type OutputLength = 'brief' | 'moderate' | 'detailed' | 'comprehensive' | 'exhaustive'; + export type OutputStyle = 'prose' | 'bullet-points' | 'numbered-list' | 'table' | 'code' | 'mixed' | 'qa' | 'dialogue'; + export type MessageRole = 'system' | 'user' | 'assistant'; + + export interface ChatMessage { role: MessageRole; content: string; name?: string; } + export interface BuiltChatPrompt { messages: ChatMessage[]; systemPrompt: string; userPrompt?: string; metadata: Record; } + + export class ChatPromptBuilder { + // Message methods + system(content: string): this; + user(content: string, name?: string): this; + assistant(content: string): this; + message(role: MessageRole, content: string, name?: string): this; + messages(messages: ChatMessage[]): this; + conversation(turns: Array<{ user: string; assistant?: string }>): this; + // Persona methods + persona(settings: string | { role?: string; tone?: PersonaTone | PersonaTone[]; expertise?: PersonaExpertise | PersonaExpertise[]; }): this; + role(role: string): this; + tone(tone: PersonaTone | PersonaTone[]): this; + expertise(expertise: PersonaExpertise | PersonaExpertise[]): this; + personality(traits: string[]): this; + background(background: string): this; + speakAs(name: string): this; + responseLanguage(language: string): this; + // Context methods + context(settings: string | { background?: string; domain?: string; audience?: string; }): this; + domain(domain: string): this; + audience(audience: string): this; + purpose(purpose: string): this; + constraints(constraints: string[]): this; + constraint(constraint: string): this; + assumptions(assumptions: string[]): this; + knowledge(facts: string[]): this; + // Task methods + task(instruction: string): this; + instruction(instruction: string): this; + steps(steps: string[]): this; + deliverables(deliverables: string[]): this; + criteria(criteria: string[]): this; + avoid(antiPatterns: string[]): this; + priority(priority: 'accuracy' | 'speed' | 'creativity' | 'thoroughness'): this; + // Example methods + example(input: string, output: string, explanation?: string): this; + examples(examples: Array<{ input: string; output: string; explanation?: string }>): this; + fewShot(examples: Array<{ input: string; output: string }>): this; + // Output methods + outputFormat(format: 'text' | 'json' | 'markdown' | 'code' | 'table'): this; + json(schema?: { name: string; schema: Record }): this; + jsonSchema(name: string, schema: Record, description?: string): this; + markdown(): this; + code(language?: string): this; + table(): this; + length(length: OutputLength): this; + style(style: OutputStyle): this; + brief(): this; + moderate(): this; + detailed(): this; + comprehensive(): this; + exhaustive(): this; + withExamples(): this; + withExplanation(): this; + withSources(): this; + withConfidence(): this; + // Reasoning methods + reasoningStyle(style: ReasoningStyle): this; + stepByStep(): this; + chainOfThought(): this; + treeOfThought(): this; + firstPrinciples(): this; + devilsAdvocate(): this; + showWork(): this; + verifyAnswer(): this; + considerAlternatives(): this; + // Build + build(): BuiltChatPrompt; + } + + export function chat(): ChatPromptBuilder; + export const chatPresets: Record ChatPromptBuilder>; + + // ============================================================================ + // VIDEO BUILDER TYPES + // ============================================================================ + + export type ShotType = 'extreme-close-up' | 'close-up' | 'medium-close-up' | 'medium' | 'medium-wide' | 'wide' | 'extreme-wide' | 'establishing' | 'full-body' | 'portrait' | 'headshot'; + export type CameraAngle = 'eye-level' | 'low-angle' | 'high-angle' | 'dutch-angle' | 'birds-eye' | 'worms-eye' | 'over-the-shoulder' | 'point-of-view' | 'aerial' | 'drone' | 'ground-level'; + export type CameraMovement = 'static' | 'pan' | 'tilt' | 'dolly' | 'truck' | 'pedestal' | 'zoom' | 'handheld' | 'steadicam' | 'crane' | 'drone' | 'tracking' | 'arc' | 'whip-pan' | 'roll' | 'boom' | 'jib' | 'snorricam' | 'vertigo-effect' | 'crash-zoom' | 'slow-push' | 'slow-pull'; + export type LensType = 'wide-angle' | 'ultra-wide' | 'standard' | 'telephoto' | 'macro' | 'fisheye' | '14mm' | '24mm' | '35mm' | '50mm' | '85mm' | '100mm' | '135mm' | '200mm' | 'tilt-shift' | 'anamorphic' | 'spherical' | 'prime' | 'zoom'; + export type LightingType = 'natural' | 'studio' | 'dramatic' | 'soft' | 'hard' | 'diffused' | 'key' | 'fill' | 'rim' | 'backlit' | 'silhouette' | 'rembrandt' | 'split' | 'butterfly' | 'loop' | 'broad' | 'short' | 'chiaroscuro' | 'high-key' | 'low-key' | 'three-point' | 'practical' | 'motivated'; + export type TimeOfDay = 'dawn' | 'sunrise' | 'golden-hour' | 'morning' | 'midday' | 'afternoon' | 'blue-hour' | 'sunset' | 'dusk' | 'twilight' | 'night' | 'midnight'; + export type ArtStyle = 'photorealistic' | 'hyperrealistic' | 'cinematic' | 'documentary' | 'editorial' | 'fashion' | 'portrait' | 'landscape' | 'street' | 'fine-art' | 'conceptual' | 'surreal' | 'abstract' | 'minimalist' | 'vintage' | 'retro' | 'noir' | 'gothic' | 'romantic' | 'cyberpunk' | 'steampunk' | 'fantasy' | 'sci-fi' | 'anime' | 'manga' | 'comic-book' | 'illustration' | 'digital-art' | 'oil-painting' | 'watercolor' | 'sketch' | '3d-render'; + export type ColorPalette = 'warm' | 'cool' | 'neutral' | 'vibrant' | 'muted' | 'pastel' | 'neon' | 'monochrome' | 'sepia' | 'desaturated' | 'high-contrast' | 'low-contrast' | 'earthy' | 'oceanic' | 'forest' | 'sunset' | 'midnight' | 'golden'; + export type Mood = 'serene' | 'peaceful' | 'melancholic' | 'dramatic' | 'tense' | 'mysterious' | 'romantic' | 'nostalgic' | 'hopeful' | 'joyful' | 'energetic' | 'chaotic' | 'ethereal' | 'dark' | 'light' | 'whimsical' | 'eerie' | 'epic' | 'intimate'; + export type FilmStock = 'kodak-portra-400' | 'kodak-portra-800' | 'kodak-ektar-100' | 'kodak-tri-x-400' | 'kodak-vision3-500t' | 'fujifilm-pro-400h' | 'fujifilm-velvia-50' | 'ilford-hp5-plus' | 'cinestill-800t' | 'cinestill-50d'; + export type FocusType = 'shallow' | 'deep' | 'soft-focus' | 'tilt-shift' | 'rack-focus' | 'split-diopter' | 'selective' | 'bokeh-heavy' | 'tack-sharp'; + export type BokehStyle = 'smooth' | 'creamy' | 'swirly' | 'busy' | 'soap-bubble' | 'cat-eye' | 'oval-anamorphic'; + export type FilterType = 'uv' | 'polarizer' | 'nd' | 'nd-graduated' | 'black-pro-mist' | 'white-pro-mist' | 'glimmer-glass' | 'classic-soft' | 'diffusion' | 'infrared' | 'warming' | 'cooling' | 'vintage-look'; + export type VideoPacing = 'slow' | 'medium' | 'fast' | 'variable' | 'building' | 'frenetic' | 'contemplative'; + export type VideoTransition = 'cut' | 'fade' | 'dissolve' | 'wipe' | 'morph' | 'match-cut' | 'jump-cut' | 'cross-dissolve' | 'iris' | 'push' | 'slide'; + + export interface BuiltVideoPrompt { prompt: string; structure: Record; } + + export class VideoPromptBuilder { + // Scene methods + scene(description: string | { description: string; setting?: string; timeOfDay?: TimeOfDay; weather?: string; atmosphere?: string }): this; + setting(setting: string): this; + // Subject methods + subject(main: string | { main: string; appearance?: string; clothing?: string; age?: string; count?: string }): this; + appearance(appearance: string): this; + clothing(clothing: string): this; + // Camera methods + camera(settings: { shot?: ShotType; angle?: CameraAngle; movement?: CameraMovement; lens?: LensType }): this; + shot(shot: ShotType): this; + angle(angle: CameraAngle): this; + movement(movement: CameraMovement): this; + lens(lens: LensType): this; + platform(platform: 'handheld' | 'steadicam' | 'tripod' | 'drone' | 'crane' | 'gimbal' | 'slider' | 'dolly' | 'technocrane' | 'russian-arm' | 'fpv-drone'): this; + cameraSpeed(speed: 'slow' | 'medium' | 'fast'): this; + movementDirection(direction: 'left' | 'right' | 'forward' | 'backward' | 'up' | 'down' | 'arc-left' | 'arc-right'): this; + rig(rig: string): this; + gimbal(gimbal: string): this; + cameraBrand(brand: string): this; + cameraModel(model: string): this; + sensor(sensor: string): this; + lensModel(model: string): this; + lensBrand(brand: string): this; + focalLength(length: string): this; + anamorphic(ratio?: '1.33x' | '1.5x' | '1.8x' | '2x'): this; + aperture(aperture: string): this; + frameRate(fps: 24 | 25 | 30 | 48 | 60 | 120 | 240): this; + slowMotion(enabled?: boolean): this; + shutterAngle(angle: number): this; + filter(filter: FilterType | FilterType[]): this; + filmStock(stock: FilmStock): this; + filmGrain(grain: 'none' | 'subtle' | 'moderate' | 'heavy'): this; + halation(enabled?: boolean): this; + // Lighting methods + lighting(settings: { type?: LightingType | LightingType[]; time?: TimeOfDay; direction?: string; intensity?: string }): this; + lightingType(type: LightingType | LightingType[]): this; + timeOfDay(time: TimeOfDay): this; + weather(weather: 'sunny' | 'cloudy' | 'overcast' | 'foggy' | 'misty' | 'rainy' | 'stormy' | 'snowy' | 'hazy'): this; + // Action methods + action(action: string, options?: { duration?: number; timing?: 'start' | 'middle' | 'end' }): this; + actions(actions: string[]): this; + motion(settings: { subject?: string; type?: string; direction?: string; speed?: string; beats?: string[] }): this; + motionBeats(beats: string[]): this; + // Style methods + style(settings: { format?: string; era?: string; look?: ArtStyle | ArtStyle[]; reference?: string[] }): this; + format(format: string): this; + era(era: string): this; + styleFilmStock(stock: string): this; + look(look: ArtStyle | ArtStyle[]): this; + reference(references: string[]): this; + // Color methods + color(settings: { palette?: ColorPalette | ColorPalette[]; anchors?: string[]; grade?: string }): this; + palette(palette: ColorPalette | ColorPalette[]): this; + colorAnchors(anchors: string[]): this; + colorGrade(grade: string): this; + // Audio methods + audio(settings: { ambient?: string; diegetic?: string[]; music?: string; dialogue?: string; soundEffects?: string[] }): this; + dialogue(dialogue: string): this; + ambient(ambient: string): this; + diegetic(sounds: string[]): this; + soundEffects(effects: string[]): this; + music(music: string): this; + // Technical methods + technical(settings: { duration?: number; resolution?: string; fps?: number; aspectRatio?: string }): this; + duration(seconds: number): this; + resolution(res: '480p' | '720p' | '1080p' | '4K'): this; + fps(fps: 24 | 30 | 60): this; + aspectRatio(ratio: '16:9' | '21:9' | '4:3' | '1:1' | '9:16'): this; + // Mood and pacing + mood(mood: Mood | Mood[]): this; + pacing(pacing: VideoPacing): this; + transition(transition: VideoTransition): this; + transitions(transitions: VideoTransition[]): this; + custom(text: string): this; + build(): BuiltVideoPrompt; + } + + export function video(): VideoPromptBuilder; + + // ============================================================================ + // AUDIO BUILDER TYPES + // ============================================================================ + + export type MusicGenre = 'pop' | 'rock' | 'jazz' | 'classical' | 'electronic' | 'hip-hop' | 'r&b' | 'country' | 'folk' | 'blues' | 'metal' | 'punk' | 'indie' | 'alternative' | 'ambient' | 'lo-fi' | 'synthwave' | 'orchestral' | 'cinematic' | 'world' | 'latin' | 'reggae' | 'soul' | 'funk' | 'disco' | 'house' | 'techno' | 'edm' | 'trap' | 'drill' | 'k-pop' | 'j-pop' | 'bossa-nova' | 'gospel' | 'grunge' | 'shoegaze' | 'post-rock' | 'prog-rock' | 'psychedelic' | 'chillwave' | 'vaporwave' | 'drum-and-bass' | 'dubstep' | 'trance' | 'hardcore'; + export type Instrument = 'piano' | 'guitar' | 'acoustic-guitar' | 'electric-guitar' | 'bass' | 'drums' | 'violin' | 'cello' | 'viola' | 'flute' | 'saxophone' | 'trumpet' | 'trombone' | 'synthesizer' | 'organ' | 'harp' | 'percussion' | 'strings' | 'brass' | 'woodwinds' | 'choir' | 'vocals' | 'beatbox' | 'turntables' | 'harmonica' | 'banjo' | 'ukulele' | 'mandolin' | 'accordion' | 'marimba' | 'vibraphone' | 'xylophone' | 'timpani' | 'congas' | 'bongos' | 'djembe' | 'tabla' | 'sitar' | '808' | '909' | 'moog' | 'rhodes' | 'wurlitzer' | 'mellotron' | 'theremin'; + export type VocalStyle = 'male' | 'female' | 'duet' | 'choir' | 'a-cappella' | 'spoken-word' | 'rap' | 'falsetto' | 'belting' | 'whisper' | 'growl' | 'melodic' | 'harmonized' | 'auto-tuned' | 'operatic' | 'soul' | 'breathy' | 'nasal' | 'raspy' | 'clear'; + export type VocalLanguage = 'english' | 'spanish' | 'french' | 'german' | 'italian' | 'portuguese' | 'japanese' | 'korean' | 'chinese' | 'arabic' | 'hindi' | 'russian' | 'turkish' | 'instrumental'; + export type TempoMarking = 'largo' | 'adagio' | 'andante' | 'moderato' | 'allegro' | 'vivace' | 'presto'; + export type TimeSignature = '4/4' | '3/4' | '6/8' | '2/4' | '5/4' | '7/8' | '12/8'; + export type MusicalKey = 'C' | 'C#' | 'Db' | 'D' | 'D#' | 'Eb' | 'E' | 'F' | 'F#' | 'Gb' | 'G' | 'G#' | 'Ab' | 'A' | 'A#' | 'Bb' | 'B' | 'Cm' | 'C#m' | 'Dm' | 'D#m' | 'Ebm' | 'Em' | 'Fm' | 'F#m' | 'Gm' | 'G#m' | 'Am' | 'A#m' | 'Bbm' | 'Bm'; + export type SongSection = 'intro' | 'verse' | 'pre-chorus' | 'chorus' | 'bridge' | 'breakdown' | 'drop' | 'build-up' | 'outro' | 'solo' | 'interlude' | 'hook'; + export type ProductionStyle = 'lo-fi' | 'hi-fi' | 'vintage' | 'modern' | 'polished' | 'raw' | 'organic' | 'synthetic' | 'acoustic' | 'electric' | 'hybrid' | 'minimalist' | 'maximalist' | 'layered' | 'sparse' | 'dense' | 'atmospheric' | 'punchy' | 'warm' | 'bright'; + export type Era = '1950s' | '1960s' | '1970s' | '1980s' | '1990s' | '2000s' | '2010s' | '2020s' | 'retro' | 'vintage' | 'classic' | 'modern' | 'futuristic'; + + export interface BuiltAudioPrompt { prompt: string; structure: Record; } + + export class AudioPromptBuilder { + // Genre methods + genre(primary: MusicGenre | { primary: MusicGenre; secondary?: MusicGenre[]; subgenre?: string }): this; + subgenre(subgenre: string): this; + fusion(genres: MusicGenre[]): this; + // Mood methods + mood(primary: string, ...secondary: string[]): this; + energy(level: 'low' | 'medium' | 'high' | 'building' | 'dynamic'): this; + emotion(emotion: string): this; + // Tempo methods + tempo(bpmOrSettings: number | { bpm?: number; marking?: string; feel?: string }): this; + bpm(bpm: number): this; + tempoMarking(marking: 'largo' | 'adagio' | 'andante' | 'moderato' | 'allegro' | 'vivace' | 'presto'): this; + tempoFeel(feel: 'steady' | 'driving' | 'relaxed' | 'swinging' | 'syncopated'): this; + // Vocal methods + vocals(settings: { style?: VocalStyle | VocalStyle[]; language?: string; theme?: string; delivery?: string }): this; + vocalStyle(style: VocalStyle | VocalStyle[]): this; + language(language: string): this; + lyrics(lyrics: string): this; + lyricsTheme(theme: string): this; + delivery(delivery: string): this; + instrumental(): this; + // Instrumentation methods + instruments(instruments: Instrument[]): this; + instrumentation(settings: { lead?: Instrument | Instrument[]; rhythm?: Instrument[]; bass?: Instrument }): this; + leadInstrument(instrument: Instrument | Instrument[]): this; + rhythmSection(instruments: Instrument[]): this; + bassInstrument(instrument: Instrument): this; + percussion(instruments: Instrument | Instrument[]): this; + pads(instruments: Instrument | Instrument[]): this; + featuredInstrument(instrument: Instrument): this; + // Structure methods + structure(settings: { sections?: Array<{ type: string; bars?: number; description?: string }>; duration?: number }): this; + section(type: 'intro' | 'verse' | 'pre-chorus' | 'chorus' | 'bridge' | 'drop' | 'outro', bars?: number, description?: string): this; + form(form: string): this; + duration(seconds: number): this; + // Production methods + production(settings: { style?: string | string[]; era?: string; reference?: string[] }): this; + productionStyle(style: string | string[]): this; + era(era: string): this; + reference(artists: string[]): this; + texture(texture: string): this; + effects(effects: string[]): this; + // Technical methods + key(key: string): this; + timeSignature(sig: '4/4' | '3/4' | '6/8' | '5/4' | '7/8'): this; + // Tags + tag(tag: string): this; + tags(tags: string[]): this; + custom(text: string): this; + build(): BuiltAudioPrompt; + } + + export function audio(): AudioPromptBuilder; + + // ============================================================================ + // IMAGE BUILDER TYPES + // ============================================================================ + + export interface BuiltImagePrompt { prompt: string; structure: Record; } + + export class ImagePromptBuilder { + // Subject methods + subject(main: string | { main: string; details?: string[]; expression?: string; pose?: string }): this; + subjectDetails(details: string[]): this; + expression(expression: string): this; + pose(pose: string): this; + action(action: string): this; + clothing(clothing: string): this; + accessories(accessories: string[]): this; + subjectCount(count: number | 'single' | 'couple' | 'group' | 'crowd'): this; + // Environment methods + environment(setting: string | { setting: string; location?: string; atmosphere?: string; season?: string }): this; + location(location: string): this; + props(props: string[]): this; + atmosphere(atmosphere: string): this; + season(season: 'spring' | 'summer' | 'autumn' | 'winter'): this; + // Camera methods + camera(settings: { angle?: CameraAngle; shot?: ShotType; lens?: LensType; focus?: FocusType }): this; + angle(angle: CameraAngle): this; + shot(shot: ShotType): this; + lens(lens: LensType): this; + focus(focus: FocusType): this; + aperture(aperture: string): this; + filmStock(filmStock: FilmStock): this; + filmFormat(format: string): this; + cameraBrand(brand: string): this; + cameraModel(model: string): this; + sensor(sensor: string): this; + lensModel(model: string): this; + lensBrand(brand: string): this; + focalLength(length: string): this; + bokeh(style: BokehStyle): this; + filter(filter: FilterType | FilterType[]): this; + iso(iso: number): this; + shutterSpeed(speed: string): this; + whiteBalance(wb: 'daylight' | 'cloudy' | 'tungsten' | 'fluorescent' | 'flash' | 'custom'): this; + colorProfile(profile: string): this; + // Lighting methods + lighting(settings: { type?: LightingType | LightingType[]; time?: TimeOfDay; direction?: string; intensity?: string }): this; + lightingType(type: LightingType | LightingType[]): this; + timeOfDay(time: TimeOfDay): this; + weather(weather: 'sunny' | 'cloudy' | 'overcast' | 'foggy' | 'misty' | 'rainy' | 'stormy' | 'snowy' | 'hazy'): this; + lightDirection(direction: 'front' | 'side' | 'back' | 'top' | 'bottom' | 'three-quarter'): this; + lightIntensity(intensity: 'soft' | 'medium' | 'hard' | 'dramatic'): this; + // Composition methods + composition(settings: { ruleOfThirds?: boolean; goldenRatio?: boolean; symmetry?: string }): this; + ruleOfThirds(): this; + goldenRatio(): this; + symmetry(type: 'none' | 'horizontal' | 'vertical' | 'radial'): this; + foreground(fg: string): this; + midground(mg: string): this; + background(bg: string): this; + // Style methods + style(settings: { medium?: ArtStyle | ArtStyle[]; artist?: string | string[] }): this; + medium(medium: ArtStyle | ArtStyle[]): this; + artist(artist: string | string[]): this; + influence(influences: string[]): this; + // Color methods + color(settings: { palette?: ColorPalette | ColorPalette[]; primary?: string[]; accent?: string[] }): this; + palette(palette: ColorPalette | ColorPalette[]): this; + primaryColors(colors: string[]): this; + accentColors(colors: string[]): this; + colorGrade(grade: string): this; + // Technical methods + technical(settings: { aspectRatio?: string; resolution?: string; quality?: string }): this; + aspectRatio(ratio: '1:1' | '4:3' | '3:2' | '16:9' | '21:9' | '9:16'): this; + resolution(resolution: string): this; + quality(quality: 'draft' | 'standard' | 'high' | 'ultra' | 'masterpiece'): this; + // Mood and misc + mood(mood: string | string[]): this; + negative(items: string[]): this; + custom(text: string): this; + build(): BuiltImagePrompt; + } + + export function image(): ImagePromptBuilder; + + // ============================================================================ + // UTILITY MODULES + // ============================================================================ + + export namespace variables { + export function detect(text: string): Array<{ name: string; pattern: string }>; + export function normalize(text: string): string; + export function extractVariables(text: string): Array<{ name: string; defaultValue?: string }>; + export function compile(text: string, values: Record): string; + } + + export namespace similarity { + export function calculate(content1: string, content2: string): number; + export function isDuplicate(content1: string, content2: string, threshold?: number): boolean; + export function findDuplicates(prompts: string[], threshold?: number): string[][]; + export function deduplicate(prompts: string[], threshold?: number): string[]; + } + + export namespace quality { + export function check(content: string): { score: number; issues: Array<{ type: string; message: string }> }; + export function validate(content: string): boolean; + export function getSuggestions(content: string): string[]; + } + + export namespace parser { + export function parse(content: string): { type: string; content: unknown }; + export function toYaml(obj: unknown): string; + export function toJson(obj: unknown): string; + } +} +`; + +function ApiDocsSidebar({ selectedItem, onSelectItem }: { selectedItem: ApiItem | null; onSelectItem: (item: ApiItem | null) => void }) { + const [expandedSections, setExpandedSections] = useState>(new Set(["Builder", "PromptBuilder Methods"])); + + const toggleSection = (name: string) => { + setExpandedSections(prev => { + const next = new Set(prev); + if (next.has(name)) { + next.delete(name); + } else { + next.add(name); + } + return next; + }); + }; + + const getTypeColor = (type: ApiItem["type"]) => { + switch (type) { + case "function": return "text-blue-500 dark:text-blue-400"; + case "class": return "text-yellow-500 dark:text-yellow-400"; + case "interface": return "text-green-500 dark:text-green-400"; + case "type": return "text-purple-500 dark:text-purple-400"; + case "const": return "text-orange-500 dark:text-orange-400"; + default: return "text-muted-foreground"; + } + }; + + const getTypeLabel = (type: ApiItem["type"]) => { + switch (type) { + case "function": return "fn"; + case "class": return "class"; + case "interface": return "interface"; + case "type": return "type"; + case "const": return "const"; + default: return type; + } + }; + + return ( +
+
+ + API Docs +
+ +
+ {API_DOCS.map((section) => ( +
+ + {expandedSections.has(section.name) && ( +
+ {section.items.map((item) => ( + + ))} +
+ )} +
+ ))} +
+
+
+ ); +} + +function ApiDetailsPanel({ item, onClose }: { item: ApiItem; onClose: () => void }) { + const getTypeColor = (type: ApiItem["type"]) => { + switch (type) { + case "function": return "text-blue-500 dark:text-blue-400"; + case "class": return "text-yellow-500 dark:text-yellow-400"; + case "interface": return "text-green-500 dark:text-green-400"; + case "type": return "text-purple-500 dark:text-purple-400"; + case "const": return "text-orange-500 dark:text-orange-400"; + default: return "text-muted-foreground"; + } + }; + + return ( +
+
+
+
+
+ {item.type} + {item.name} +
+ {item.signature && ( + {item.signature} + )} +
+ +
+ {item.description && ( +

{item.description}

+ )} + {item.params && item.params.length > 0 && ( +
+

Parameters:

+
+ {item.params.map((p) => ( +
+ {p.name} + : {p.type} + — {p.description} +
+ ))} +
+
+ )} + {item.returns && ( +

Returns: {item.returns}

+ )} + {item.example && ( +
+

Example:

+
{item.example}
+
+ )} +
+
+ ); +} + +export function PromptIde() { + const t = useTranslations("ide"); + const { theme } = useTheme(); + const [code, setCode] = useState(DEFAULT_CODE); + const [output, setOutput] = useState(""); + const [outputFormat, setOutputFormat] = useState("json"); + const [error, setError] = useState(null); + const [isRunning, setIsRunning] = useState(false); + const [selectedApiItem, setSelectedApiItem] = useState(null); + + const runCode = useCallback(() => { + setIsRunning(true); + setError(null); + + try { + // Transform code: strip imports and handle the module-style code + let transformedCode = code + // Remove import statements + .replace(/^import\s+.*?from\s+['"]prompts\.chat['"];?\s*$/gm, '') + .replace(/^import\s+.*?from\s+['"]prompts\.chat\/.*?['"];?\s*$/gm, '') + // Remove export statements but keep the content + .replace(/^export\s+/gm, '') + .trim(); + + // Find the last expression (standalone identifier or expression) and return it + const lines = transformedCode.split('\n'); + const lastLine = lines[lines.length - 1].trim(); + + // If the last line is a simple identifier or expression (not a statement), wrap it in return + if (lastLine && !lastLine.endsWith(';') && !lastLine.startsWith('//') && !lastLine.startsWith('/*')) { + lines[lines.length - 1] = `return ${lastLine}`; + transformedCode = lines.join('\n'); + } else if (lastLine.endsWith(';') && !lastLine.includes('=') && !lastLine.startsWith('const ') && !lastLine.startsWith('let ') && !lastLine.startsWith('var ')) { + // Last line is an expression statement like "prompt;" - convert to return + lines[lines.length - 1] = `return ${lastLine.slice(0, -1)}`; + transformedCode = lines.join('\n'); + } + + // Wrap the code to capture the result + const wrappedCode = ` + ${transformedCode} + `; + + // Execute the code with the actual prompts.chat library + const fn = new Function( + 'builder', 'fromPrompt', 'templates', + 'video', 'audio', 'image', 'chat', 'chatPresets', + 'variables', 'similarity', 'quality', 'parser', + wrappedCode + ); + const result = fn( + builder, fromPrompt, templates, + video, audio, image, chat, chatPresets, + variables, similarity, quality, parser + ); + + // Format output based on selected format + formatOutput(result); + } catch (err) { + setError(err instanceof Error ? err.message : "An error occurred"); + setOutput(""); + } finally { + setIsRunning(false); + } + }, [code, outputFormat]); + + const formatOutput = useCallback((result: unknown) => { + if (!result) { + setOutput(""); + return; + } + + try { + switch (outputFormat) { + case "json": + setOutput(JSON.stringify(result, null, 2)); + break; + case "yaml": + setOutput(toYaml(result)); + break; + case "markdown": + if (typeof result === 'string') { + setOutput(result); + } else if (typeof result === 'object' && result !== null) { + // Try common prompt result properties + if ('content' in result) { + setOutput((result as { content: string }).content); + } else if ('prompt' in result) { + setOutput((result as { prompt: string }).prompt); + } else if ('systemPrompt' in result) { + setOutput((result as { systemPrompt: string }).systemPrompt); + } else { + // Fallback to JSON for objects without known text properties + setOutput(JSON.stringify(result, null, 2)); + } + } else { + setOutput(String(result)); + } + break; + } + } catch { + setError("Failed to format output"); + } + }, [outputFormat]); + + // Simple YAML serializer + const toYaml = (obj: unknown, indent = 0): string => { + const spaces = ' '.repeat(indent); + + if (obj === null || obj === undefined) return 'null'; + if (typeof obj === 'string') { + if (obj.includes('\n')) { + return `|\n${obj.split('\n').map(line => spaces + ' ' + line).join('\n')}`; + } + return obj.includes(':') || obj.includes('#') ? `"${obj.replace(/"/g, '\\"')}"` : obj; + } + if (typeof obj === 'number' || typeof obj === 'boolean') return String(obj); + + if (Array.isArray(obj)) { + if (obj.length === 0) return '[]'; + return obj.map(item => { + if (typeof item === 'object' && item !== null) { + const inner = toYaml(item, indent + 1); + const lines = inner.split('\n'); + return `${spaces}- ${lines[0]}\n${lines.slice(1).map(l => spaces + ' ' + l).join('\n')}`.trim(); + } + return `${spaces}- ${toYaml(item, indent)}`; + }).join('\n'); + } + + if (typeof obj === 'object') { + const entries = Object.entries(obj).filter(([, v]) => v !== undefined); + if (entries.length === 0) return '{}'; + return entries.map(([key, value]) => { + if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + return `${spaces}${key}:\n${toYaml(value, indent + 1)}`; + } + if (Array.isArray(value)) { + return `${spaces}${key}:\n${toYaml(value, indent + 1)}`; + } + return `${spaces}${key}: ${toYaml(value, indent)}`; + }).join('\n'); + } + + return String(obj); + }; + + // Re-run when output format changes + useEffect(() => { + if (output || error) { + runCode(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [outputFormat]); + + const handleEditorMount = useCallback((_editor: unknown, monaco: unknown) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const m = monaco as any; + + // Add custom type definitions for prompts.chat + m.languages?.typescript?.typescriptDefaults?.addExtraLib( + TYPE_DEFINITIONS, + 'prompts.chat.d.ts' + ); + + // Configure TypeScript compiler options + m.languages?.typescript?.typescriptDefaults?.setCompilerOptions({ + target: 99, // ESNext + allowNonTsExtensions: true, + moduleResolution: 2, // NodeJs + module: 99, // ESNext + noEmit: true, + esModuleInterop: true, + allowSyntheticDefaultImports: true, + strict: true, + }); + }, []); + + const copyOutput = useCallback(() => { + navigator.clipboard.writeText(output); + toast.success(t("copied")); + }, [output, t]); + + const resetCode = useCallback(() => { + setCode(DEFAULT_CODE); + setOutput(""); + setError(null); + }, []); + + return ( +
+ {/* Toolbar */} +
+
+ +
+

{t("title")}

+

{t("subtitle")}

+
+
+
+ + +
+
+ + {/* Main content */} +
+ {/* API Docs sidebar */} +
+ + {selectedApiItem && ( + setSelectedApiItem(null)} /> + )} +
+ + {/* Editor panel */} +
+
+ {t("editor")} +
+ + + + +
+
+
+ setCode(value || "")} + theme={theme === "dark" ? "vs-dark" : "light"} + onMount={handleEditorMount} + options={{ + minimap: { enabled: false }, + fontSize: 14, + lineNumbers: "on", + scrollBeyondLastLine: false, + automaticLayout: true, + tabSize: 2, + wordWrap: "on", + padding: { top: 16, bottom: 16 }, + }} + /> +
+
+ + {/* Preview panel */} +
+
+ {t("preview")} +
+ setOutputFormat(v as OutputFormat)}> + + + + JSON + + + + YAML + + + + MD + + + + {output && ( + + )} +
+
+
+ {error ? ( +
+

{error}

+
+ ) : output ? ( + + ) : ( +
+ +

{t("runToPreview")}

+
+ )} +
+
+
+
+ ); +} diff --git a/src/components/layout/header.tsx b/src/components/layout/header.tsx index bd4f33d1..cb6c5292 100644 --- a/src/components/layout/header.tsx +++ b/src/components/layout/header.tsx @@ -174,6 +174,13 @@ export function Header({ authProvider = "credentials", allowRegistration = true > {t("nav.promptmasters")} + setMobileMenuOpen(false)} + className="flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-accent transition-colors" + > + {t("nav.ide")} +
@@ -282,6 +289,12 @@ export function Header({ authProvider = "credentials", allowRegistration = true > {t("nav.promptmasters")} + + {t("nav.ide")} + {/* Spacer */}