From fb68ba1f023a035c1ba9d72b79d7b163fe2b9f03 Mon Sep 17 00:00:00 2001 From: GeiserX <9169332+GeiserX@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:53:28 +0100 Subject: [PATCH 1/4] feat: wizard enhancements, persona fix, paid template variable preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add MCP servers config to CLI and WebUI wizard - Add workaround behavior toggle (attempt vs stop-and-ask) - Add SSH access and manual deployment questions (conditional) - Add Burke Holland-inspired AI behavior rules (code_for_llms, self_improving, verify_work, terminal_management, check_docs_first) - Show customizable variables in paid template previews - Fix persona wording: "Developer background: X. Adapt..." instead of "I am X" β€” properly describes the developer's expertise - Refactor CLI wizard to import from shared package (languages, frameworks, databases, test frameworks) - Add Socialify banner to README - Clean up remaining ClickHouse references from docs - Bump version to 1.6.0 --- CHANGELOG.md | 19 ++- README.md | 8 +- cli/src/commands/wizard.ts | 192 ++++++++++++++------------- cli/src/utils/generator.ts | 72 +++++++++- docs/ROADMAP.md | 6 +- package.json | 2 +- src/app/api/blueprints/[id]/route.ts | 15 +++ src/app/blueprints/[id]/page.tsx | 25 ++++ src/app/wizard/page.tsx | 143 +++++++++++++++++++- src/lib/file-generator.ts | 64 ++++++++- 10 files changed, 432 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 755a1731..01f67225 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,24 @@ All notable changes to LynxPrompt will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [1.6.0] - February 2026 + +### Added +- **MCP Servers config**: New wizard field (CLI + WebUI) to list MCP servers the developer uses. Generated output tells the AI to use them when relevant. +- **Workaround behavior toggle**: New toggle to control whether the AI should attempt creative workarounds when stuck, or stop and ask. +- **Infrastructure questions**: Conditional wizard questions for SSH server access (key path) and manual deployment method (Portainer, Docker Compose, Kubernetes, bare metal). +- **Burke Holland-inspired AI rules**: New behavior options β€” "Code for LLMs", "Self-Improving Config", "Always Verify Work", "Terminal Management", "Check Docs First". +- **Paid template variable preview**: Locked paid templates now show their customizable variables (name + default) so users can see what they'll get before purchasing. +- **Socialify banner**: Added dynamic Socialify image to README for better GitHub social previews. +- **XCTest support**: Now available in test frameworks (via shared package import). + +### Changed +- **Persona wording**: Generated output now uses "Developer background: X. Adapt your suggestions..." instead of "I am X" or "You are X" β€” properly tells the AI about the developer's expertise without roleplaying. +- **CLI wizard refactored**: Languages, frameworks, databases, and test frameworks now imported from the shared package instead of being hardcoded in the CLI. +- **CLI agent sessions hint**: Improved explanation for the "multiple AI agent sessions" wizard question. + +### Removed +- **ClickHouse references**: Cleaned up remaining ClickHouse mentions from ROADMAP.md documentation. --- diff --git a/README.md b/README.md index ac1478e9..41d3f6b2 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ - - - - LynxPrompt - + + LynxPrompt + # LynxPrompt diff --git a/cli/src/commands/wizard.ts b/cli/src/commands/wizard.ts index 3b8996ec..7ea04741 100644 --- a/cli/src/commands/wizard.ts +++ b/cli/src/commands/wizard.ts @@ -9,6 +9,12 @@ import { detectProject, detectFromRemoteUrl, isGitUrl } from "../utils/detect.js import { generateConfig, GenerateOptions, parseVariablesString } from "../utils/generator.js"; import { isAuthenticated, getUser } from "../config.js"; import { api, ApiRequestError } from "../api.js"; +import { + LANGUAGES as SHARED_LANGUAGES, + FRAMEWORKS as SHARED_FRAMEWORKS, + DATABASES as SHARED_DATABASES, + TEST_FRAMEWORKS as SHARED_TEST_FRAMEWORKS, +} from "../utils/wizard-options.js"; // Draft management - local storage in .lynxprompt/drafts/ const DRAFTS_DIR = ".lynxprompt/drafts"; @@ -238,52 +244,10 @@ const ALL_PLATFORMS = [ ]; -// Languages -const LANGUAGES = [ - { title: "πŸ”· TypeScript", value: "typescript" }, - { title: "🟑 JavaScript", value: "javascript" }, - { title: "🐍 Python", value: "python" }, - { title: "πŸ”΅ Go", value: "go" }, - { title: "πŸ¦€ Rust", value: "rust" }, - { title: "β˜• Java", value: "java" }, - { title: "πŸ’œ C#/.NET", value: "csharp" }, - { title: "πŸ’Ž Ruby", value: "ruby" }, - { title: "🐘 PHP", value: "php" }, - { title: "🍎 Swift", value: "swift" }, - { title: "πŸ”Ά Kotlin", value: "kotlin" }, - { title: "⬛ C/C++", value: "cpp" }, -]; - -// Frameworks -const FRAMEWORKS = [ - { title: "βš›οΈ React", value: "react" }, - { title: "β–² Next.js", value: "nextjs" }, - { title: "πŸ’š Vue.js", value: "vue" }, - { title: "πŸ…°οΈ Angular", value: "angular" }, - { title: "πŸ”₯ Svelte", value: "svelte" }, - { title: "πŸš‚ Express", value: "express" }, - { title: "⚑ FastAPI", value: "fastapi" }, - { title: "🎸 Django", value: "django" }, - { title: "πŸ§ͺ Flask", value: "flask" }, - { title: "πŸƒ Spring", value: "spring" }, - { title: "πŸ’Ž Rails", value: "rails" }, - { title: "πŸ”΄ Laravel", value: "laravel" }, - { title: "πŸ—οΈ NestJS", value: "nestjs" }, - { title: "⚑ Vite", value: "vite" }, - { title: "πŸ“± React Native", value: "react-native" }, -]; - -// Databases -const DATABASES = [ - { title: "🐘 PostgreSQL", value: "postgresql" }, - { title: "🐬 MySQL", value: "mysql" }, - { title: "πŸƒ MongoDB", value: "mongodb" }, - { title: "πŸ”΄ Redis", value: "redis" }, - { title: "πŸ“Š SQLite", value: "sqlite" }, - { title: "☁️ Supabase", value: "supabase" }, - { title: "πŸ”₯ Firebase", value: "firebase" }, - { title: "πŸ“‚ Prisma", value: "prisma" }, -]; +// Languages, frameworks, and databases imported from shared package (wizard-options.ts) +const LANGUAGES = SHARED_LANGUAGES; +const FRAMEWORKS = SHARED_FRAMEWORKS; +const DATABASES = SHARED_DATABASES; // Package managers (JS/TS only) const PACKAGE_MANAGERS = [ @@ -641,6 +605,12 @@ const AI_BEHAVIOR_RULES = [ { id: "run_tests_before_commit", label: "Run Tests Before Commit", description: "AI must run the test suite and ensure all tests pass before suggesting a commit", recommended: true }, { id: "follow_existing_patterns", label: "Follow Existing Patterns", description: "AI should study existing code and follow the same naming, structure, and conventions used in the codebase", recommended: true }, { id: "ask_before_large_refactors", label: "Ask Before Large Refactors", description: "AI should always ask for explicit approval before making significant architectural changes or refactoring multiple files", recommended: true }, + // Burke Holland-inspired rules + { id: "code_for_llms", label: "Code for LLMs", description: "Optimize code for LLM reasoning: flat/explicit patterns, minimal abstractions, structured logging" }, + { id: "self_improving", label: "Self-Improving Config", description: "AI updates this config file when it learns new project patterns or conventions" }, + { id: "verify_work", label: "Always Verify Work", description: "Run tests, check builds, and confirm changes work before returning control", recommended: true }, + { id: "terminal_management", label: "Terminal Management", description: "Reuse existing terminals and close unused ones" }, + { id: "check_docs_first", label: "Check Docs First", description: "Check documentation via MCP or project docs before assuming knowledge about APIs" }, ]; // ═══════════════════════════════════════════════════════════════ @@ -789,49 +759,8 @@ const BOUNDARY_OPTIONS = [ "Skip tests temporarily", ]; -// Testing frameworks - expanded to match WebUI -const TEST_FRAMEWORKS = [ - // JavaScript/TypeScript - "jest", "vitest", "mocha", "ava", "tap", "bun:test", - // E2E/Integration - "playwright", "cypress", "puppeteer", "selenium", "webdriverio", "testcafe", - // React/Frontend - "rtl", "enzyme", "storybook", "chromatic", - // API/Mocking - "msw", "supertest", "pact", "dredd", "karate", "postman", "insomnia", - // Python - "pytest", "unittest", "nose2", "hypothesis", "behave", "robot", - // Go - "go-test", "testify", "ginkgo", "gomega", - // Java/JVM - "junit", "testng", "mockito", "spock", "cucumber-jvm", - // Ruby - "rspec", "minitest", "capybara", "factory_bot", - // .NET - "xunit", "nunit", "mstest", "specflow", - // Infrastructure/DevOps - "terratest", "conftest", "opa", "inspec", "serverspec", "molecule", "kitchen", "goss", - // Kubernetes - "kubetest", "kuttl", "chainsaw", "helm-unittest", - // Security - "owasp-zap", "burpsuite", "nuclei", "semgrep", - // Load/Performance - "k6", "locust", "jmeter", "artillery", "gatling", "vegeta", "wrk", "ab", - // Chaos Engineering - "chaos-mesh", "litmus", "gremlin", "toxiproxy", - // Contract Testing - "spring-cloud-contract", "specmatic", - // BDD - "cucumber", "gauge", "concordion", - // Mutation Testing - "stryker", "pitest", "mutmut", - // Fuzzing - "go-fuzz", "afl", "libfuzzer", "jazzer", - // PHP - "phpunit", "pest", "codeception", - // Rust - "cargo-test", "rstest", "proptest", -]; +// Testing frameworks imported from shared package (includes XCTest, Espresso, etc.) +const TEST_FRAMEWORKS: string[] = SHARED_TEST_FRAMEWORKS; // Test levels const TEST_LEVELS = [ @@ -2250,7 +2179,7 @@ async function runInteractiveWizard( name: "useGitWorktrees", message: chalk.white("🌲 Do you plan on working with several AI agent sessions in this repository?"), initial: true, - hint: "If yes, AI will be instructed to always use git worktrees for each task", + hint: "Enable if you use multiple AI agents (Cursor, Claude, Copilot) in parallel. Each task gets its own git worktree to prevent branch conflicts.", }, promptConfig); answers.useGitWorktrees = useGitWorktreesResponse.useGitWorktrees ?? true; @@ -3163,6 +3092,17 @@ async function runInteractiveWizard( }, promptConfig); answers.explanationVerbosity = verbosityResponse.explanationVerbosity || "balanced"; + // Workaround behavior + const workaroundResponse = await prompts({ + type: "toggle", + name: "attemptWorkarounds", + message: chalk.white("When stuck, should the AI attempt workarounds?"), + initial: true, + active: "Yes, try workarounds", + inactive: "No, stop and ask", + }, promptConfig); + answers.attemptWorkarounds = workaroundResponse.attemptWorkarounds ?? true; + // Focus areas const accessibilityResponse = await prompts({ type: "toggle", @@ -3184,6 +3124,68 @@ async function runInteractiveWizard( }, promptConfig); answers.performanceFocus = performanceResponse.performanceFocus ?? false; + // MCP Servers + console.log(); + console.log(chalk.gray(" πŸ”Œ MCP (Model Context Protocol) servers let the AI interact with external tools")); + console.log(chalk.gray(" like databases, APIs, file systems, and more. List any you have configured.")); + const mcpServersResponse = await prompts({ + type: "text", + name: "mcpServers", + message: chalk.white("MCP servers (comma-separated, or leave empty):"), + hint: chalk.gray("e.g. filesystem, github, postgres, docker"), + }, promptConfig); + answers.mcpServers = mcpServersResponse.mcpServers || ""; + + // Server access + const serverAccessResponse = await prompts({ + type: "toggle", + name: "serverAccess", + message: chalk.white("Does this project require logging into a server?"), + initial: false, + active: "Yes", + inactive: "No", + }, promptConfig); + answers.serverAccess = serverAccessResponse.serverAccess ?? false; + + if (answers.serverAccess) { + const sshKeyPathResponse = await prompts({ + type: "text", + name: "sshKeyPath", + message: chalk.white("SSH key path (leave empty for default ~/.ssh/):"), + hint: chalk.gray("e.g. ~/.ssh/id_ed25519"), + }, promptConfig); + answers.sshKeyPath = sshKeyPathResponse.sshKeyPath || ""; + } + + // Manual deployment (only ask if no CI/CD was selected) + const hasCicd = (answers.cicd as string[])?.length > 0; + if (!hasCicd) { + const manualDeployResponse = await prompts({ + type: "toggle", + name: "manualDeployment", + message: chalk.white("Do you deploy manually (no CI/CD)?"), + initial: false, + active: "Yes", + inactive: "No", + }, promptConfig); + answers.manualDeployment = manualDeployResponse.manualDeployment ?? false; + + if (answers.manualDeployment) { + const deployMethodResponse = await prompts({ + type: "select", + name: "deploymentMethod", + message: chalk.white("How do you deploy?"), + choices: [ + { title: "🐳 Portainer (GitOps stacks)", value: "portainer" }, + { title: "πŸ“¦ Docker Compose (manual)", value: "docker_compose" }, + { title: "☸️ Kubernetes (kubectl)", value: "kubernetes" }, + { title: "πŸ–₯️ Bare metal (direct)", value: "bare_metal" }, + ], + }, promptConfig); + answers.deploymentMethod = deployMethodResponse.deploymentMethod || ""; + } + } + console.log(); console.log(chalk.gray(" πŸ“ Select files the AI should read first to understand your project context.")); console.log(chalk.gray(" These help the AI understand your codebase, APIs, and conventions.")); @@ -3995,5 +3997,15 @@ async function runInteractiveWizard( additionalLibraries: answers.additionalLibraries as string, // Docker image names dockerImageNames: answers.dockerImageNames as string, + // MCP servers + mcpServers: answers.mcpServers as string, + // Workaround behavior + attemptWorkarounds: answers.attemptWorkarounds as boolean, + // Server access + serverAccess: answers.serverAccess as boolean, + sshKeyPath: answers.sshKeyPath as string, + // Manual deployment + manualDeployment: answers.manualDeployment as boolean, + deploymentMethod: answers.deploymentMethod as string, }; } diff --git a/cli/src/utils/generator.ts b/cli/src/utils/generator.ts index 788abe82..15326ca6 100644 --- a/cli/src/utils/generator.ts +++ b/cli/src/utils/generator.ts @@ -91,6 +91,16 @@ export interface GenerateOptions { additionalLibraries?: string; // comma-separated (e.g., "Telethon, APScheduler, alembic") // Docker image names dockerImageNames?: string; // comma-separated (e.g., "myuser/myapp, myuser/myapp-viewer") + // MCP servers the developer uses (e.g., "filesystem, github, postgres") + mcpServers?: string; + // Whether AI should attempt workarounds when stuck, or stop and ask + attemptWorkarounds?: boolean; + // Server/SSH access + serverAccess?: boolean; // Whether the project requires server access + sshKeyPath?: string; // SSH key path (empty = default location) + // Manual deployment (when no CI/CD) + manualDeployment?: boolean; // Whether deployment is manual (no CI/CD) + deploymentMethod?: string; // portainer, docker_compose, kubernetes, bare_metal, etc. } /** @@ -189,7 +199,7 @@ const PLATFORM_FILES: Record = { firebender: "firebender.json", }; -// Persona descriptions +// Persona descriptions - these describe the developer's background so the AI can adapt const PERSONA_DESCRIPTIONS: Record = { backend: "a senior backend developer specializing in APIs, databases, and microservices architecture", frontend: "a senior frontend developer specializing in UI components, styling, and user experience", @@ -259,6 +269,12 @@ const AI_BEHAVIOR_DESCRIPTIONS: Record = { test_first: "Write tests before implementing new functionality (TDD)", no_console: "Remove console.log/print statements before committing", type_strict: "Be strict with types - avoid any/Any/Object types", + // Burke Holland-inspired rules + code_for_llms: "Optimize code for LLM reasoning: prefer flat/explicit patterns, minimal abstractions, structured logging, and linear control flow", + self_improving: "When you learn new project patterns or conventions, suggest updates to this configuration file", + verify_work: "Always verify your work before returning: run tests, check builds, confirm changes work as expected", + terminal_management: "Reuse existing terminals when possible. Close terminals you no longer need", + check_docs_first: "Always check documentation (via MCP or project docs) before assuming knowledge about APIs or libraries", }; // Important files descriptions - must match wizard options @@ -787,16 +803,15 @@ function generateFileContent(options: GenerateOptions, platform: string): string if (isMarkdown || isMdc) { sections.push("## Persona"); sections.push(""); + sections.push(`You assist developers working on ${projectName}.`); if (personaDesc) { - sections.push(`You are ${personaDesc}. You assist developers working on ${projectName}.`); - } else { - sections.push(`You assist developers working on ${projectName}.`); + sections.push(""); + sections.push(`Developer background: ${personaDesc}. Adapt your suggestions and level of explanation to this expertise.`); } } else { + sections.push(`You assist developers working on ${projectName}.`); if (personaDesc) { - sections.push(`You are ${personaDesc}. You assist developers working on ${projectName}.`); - } else { - sections.push(`You assist developers working on ${projectName}.`); + sections.push(`Developer background: ${personaDesc}. Adapt your suggestions and level of explanation to this expertise.`); } } @@ -1018,6 +1033,12 @@ function generateFileContent(options: GenerateOptions, platform: string): string sections.push(`- ${planModeDescriptions[options.planModeFrequency]}`); } } + // Workaround behavior + if (options.attemptWorkarounds === true) { + sections.push("- When stuck, **attempt creative workarounds** before asking for help"); + } else if (options.attemptWorkarounds === false) { + sections.push("- When stuck, **stop and ask** rather than attempting workarounds"); + } sections.push(""); } } @@ -1037,6 +1058,43 @@ function generateFileContent(options: GenerateOptions, platform: string): string sections.push(""); } + // MCP Servers + if (options.mcpServers) { + const servers = options.mcpServers.split(",").map(s => s.trim()).filter(Boolean); + if (servers.length > 0 && (isMarkdown || isMdc)) { + sections.push("## MCP Servers"); + sections.push(""); + sections.push("The developer has these MCP (Model Context Protocol) servers available. Use them when relevant:"); + sections.push(""); + for (const server of servers) { + sections.push(`- ${server}`); + } + sections.push(""); + } + } + + // Server access & deployment + if ((options.serverAccess || options.manualDeployment) && (isMarkdown || isMdc)) { + sections.push("## Infrastructure"); + sections.push(""); + if (options.serverAccess) { + const keyInfo = options.sshKeyPath + ? `SSH key: \`${options.sshKeyPath}\`` + : "SSH key in default location (~/.ssh/)"; + sections.push(`- **Server access**: via SSH. ${keyInfo}`); + } + if (options.manualDeployment && options.deploymentMethod) { + const methods: Record = { + portainer: "Portainer (GitOps stacks)", + docker_compose: "Docker Compose (manual)", + kubernetes: "Kubernetes (kubectl apply)", + bare_metal: "Bare metal (direct deployment)", + }; + sections.push(`- **Deployment**: ${methods[options.deploymentMethod] || options.deploymentMethod}`); + } + sections.push(""); + } + // Important files if (options.importantFiles && options.importantFiles.length > 0) { if (isMarkdown || isMdc) { diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index a9333297..3ca1c5fe 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -126,7 +126,6 @@ Per EU Consumer Rights Directive, digital content can waive 14-day withdrawal IF - [x] Project scaffolding with Next.js 15, React 19, TypeScript - [x] PostgreSQL database with Prisma ORM (dual-database architecture) -- [x] ClickHouse for analytics (self-hosted EU) - [x] Umami analytics (self-hosted EU, cookieless) - [x] Authentication with NextAuth.js (GitHub, Google, Magic Link, Passkeys) - [x] Homepage with platform carousel @@ -437,7 +436,7 @@ When downloading, user sees: #### Template Analytics -- [ ] Track template downloads/usage (ClickHouse) +- [ ] Track template downloads/usage - [ ] Show trending templates - [ ] Usage statistics for template authors - [ ] Revenue reports for paid templates @@ -743,7 +742,6 @@ POST /api/generate - Generate config files from wizard data ### Current Infrastructure - [x] PostgreSQL (4 databases: app, users, blog, support) -- [x] ClickHouse (self-hosted EU, analytics) - [x] Umami (self-hosted EU, cookieless analytics) - [x] Docker deployment with GitOps (Portainer) - [x] Cloudflare DDoS protection and WAF @@ -760,7 +758,7 @@ POST /api/generate - Generate config files from wizard data - [ ] Annual third-party penetration test - [ ] Bug bounty program (HackerOne or similar) -> **Note:** GlitchTip is preferred over Sentry for self-hosted error tracking. It integrates well with our existing ClickHouse setup and keeps all data in EU. +> **Note:** GlitchTip is preferred over Sentry for self-hosted error tracking. It keeps all data in EU. --- diff --git a/package.json b/package.json index 21ba5c27..ad65a89a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lynxprompt", - "version": "1.5.0", + "version": "1.6.0", "private": true, "description": "LynxPrompt - Transform your development setup into a mouse-click experience with AI IDE configuration generation", "author": "LynxPrompt Contributors", diff --git a/src/app/api/blueprints/[id]/route.ts b/src/app/api/blueprints/[id]/route.ts index 838e4612..923320fb 100644 --- a/src/app/api/blueprints/[id]/route.ts +++ b/src/app/api/blueprints/[id]/route.ts @@ -109,6 +109,19 @@ export async function GET( // If not purchased AND not owner, hide the content if (isPaid && !hasPurchased && !isOwner) { + // Extract variables from content so users can see what's customizable + const variableRegex = /\[\[([A-Z_][A-Z0-9_]*)(?:\|([^\]]*))?\]\]/g; + const variables: Array<{ name: string; defaultVal?: string }> = []; + const seen = new Set(); + let match; + const content = templateWithShowcase.content || ""; + while ((match = variableRegex.exec(content)) !== null) { + if (!seen.has(match[1])) { + seen.add(match[1]); + variables.push({ name: match[1], defaultVal: match[2] || undefined }); + } + } + return NextResponse.json({ ...templateWithShowcase, content: null, // Hide content @@ -121,6 +134,8 @@ export async function GET( (templateWithShowcase.content && templateWithShowcase.content.length > 500 ? "\n\n... [Purchase to view full content]" : ""), + // Show variables even for unpurchased templates + variables: variables.length > 0 ? variables : undefined, }); } diff --git a/src/app/blueprints/[id]/page.tsx b/src/app/blueprints/[id]/page.tsx index d2685d4a..f16f8f0e 100644 --- a/src/app/blueprints/[id]/page.tsx +++ b/src/app/blueprints/[id]/page.tsx @@ -824,6 +824,31 @@ export default function BlueprintDetailPage() { + {/* Show customizable variables even for locked templates */} + {blueprint.variables && blueprint.variables.length > 0 && ( +
+

+ Customizable Variables ({blueprint.variables.length}) +

+

+ This template includes variables you can customize after purchase: +

+
+ {blueprint.variables.map((v: { name: string; defaultVal?: string }) => ( + + {v.name} + {v.defaultVal && ( + = {v.defaultVal} + )} + + ))} +
+
+ )} ) : (
diff --git a/src/app/wizard/page.tsx b/src/app/wizard/page.tsx
index 4c45985b..0f9aa0d9 100644
--- a/src/app/wizard/page.tsx
+++ b/src/app/wizard/page.tsx
@@ -283,6 +283,12 @@ const AI_BEHAVIOR_RULES = [
   { id: "run_tests_before_commit", label: "Run Tests Before Commit", description: "Ensure tests pass before committing", recommended: true },
   { id: "follow_existing_patterns", label: "Follow Existing Patterns", description: "Match the codebase's existing style", recommended: true },
   { id: "ask_before_large_refactors", label: "Ask Before Large Refactors", description: "Confirm before significant changes", recommended: true },
+  // Burke Holland-inspired rules
+  { id: "code_for_llms", label: "Code for LLMs", description: "Optimize for LLM reasoning: flat patterns, minimal abstractions" },
+  { id: "self_improving", label: "Self-Improving Config", description: "AI updates this config when it learns project patterns" },
+  { id: "verify_work", label: "Always Verify Work", description: "Run tests and check builds before returning control", recommended: true },
+  { id: "terminal_management", label: "Terminal Management", description: "Reuse existing terminals, close unused ones" },
+  { id: "check_docs_first", label: "Check Docs First", description: "Check docs via MCP before assuming API knowledge" },
 ];
 
 // ═══════════════════════════════════════════════════════════════
@@ -684,6 +690,12 @@ type WizardConfig = {
   explanationVerbosity: string; // concise, balanced, detailed
   accessibilityFocus: boolean;
   performanceFocus: boolean;
+  mcpServers: string; // Comma-separated list of MCP servers (e.g., "filesystem, github, postgres")
+  attemptWorkarounds: boolean; // Whether AI should attempt workarounds when stuck
+  serverAccess: boolean; // Whether project requires server login
+  sshKeyPath: string; // Custom SSH key path (empty = default)
+  manualDeployment: boolean; // Whether deployment is manual (no CI/CD)
+  deploymentMethod: string; // portainer, docker_compose, kubernetes, bare_metal
   importantFiles: string[];
   importantFilesOther: string;
   enableAutoUpdate: boolean;
@@ -845,11 +857,17 @@ function WizardPageContent() {
     containerRegistryOther: "",
     dockerImageNames: "",
     registryUsername: "",
-    aiBehaviorRules: ["always_debug_after_build", "check_logs_after_build", "run_tests_before_commit", "follow_existing_patterns", "ask_before_large_refactors"],
+    aiBehaviorRules: ["always_debug_after_build", "check_logs_after_build", "run_tests_before_commit", "follow_existing_patterns", "ask_before_large_refactors", "verify_work"],
     planModeFrequency: "complex_tasks",
     explanationVerbosity: "balanced",
     accessibilityFocus: false,
     performanceFocus: false,
+    mcpServers: "",
+    attemptWorkarounds: true,
+    serverAccess: false,
+    sshKeyPath: "",
+    manualDeployment: false,
+    deploymentMethod: "",
     importantFiles: [],
     importantFilesOther: "",
     enableAutoUpdate: false,
@@ -1490,6 +1508,12 @@ function WizardPageContent() {
       
       // AI behavior
       aiBehaviorRules: config.aiBehaviorRules,
+      mcpServers: config.mcpServers,
+      attemptWorkarounds: config.attemptWorkarounds,
+      serverAccess: config.serverAccess,
+      sshKeyPath: config.sshKeyPath,
+      manualDeployment: config.manualDeployment,
+      deploymentMethod: config.deploymentMethod,
       importantFiles: config.importantFiles,
       importantFilesOther: config.importantFilesOther,
       enableAutoUpdate: config.enableAutoUpdate,
@@ -2732,6 +2756,19 @@ ${syncCommands}
                   onAccessibilityFocusChange={(v) => setConfig({ ...config, accessibilityFocus: v })}
                   performanceFocus={config.performanceFocus}
                   onPerformanceFocusChange={(v) => setConfig({ ...config, performanceFocus: v })}
+                  mcpServers={config.mcpServers}
+                  onMcpServersChange={(v) => setConfig({ ...config, mcpServers: v })}
+                  attemptWorkarounds={config.attemptWorkarounds}
+                  onAttemptWorkaroundsChange={(v) => setConfig({ ...config, attemptWorkarounds: v })}
+                  serverAccess={config.serverAccess}
+                  onServerAccessChange={(v) => setConfig({ ...config, serverAccess: v })}
+                  sshKeyPath={config.sshKeyPath}
+                  onSshKeyPathChange={(v) => setConfig({ ...config, sshKeyPath: v })}
+                  manualDeployment={config.manualDeployment}
+                  onManualDeploymentChange={(v) => setConfig({ ...config, manualDeployment: v })}
+                  deploymentMethod={config.deploymentMethod}
+                  onDeploymentMethodChange={(v) => setConfig({ ...config, deploymentMethod: v })}
+                  hasCicd={(config.cicd?.length ?? 0) > 0}
                   importantFiles={config.importantFiles}
                   importantFilesOther={config.importantFilesOther}
                   onImportantFilesToggle={(v) => toggleArrayValue("importantFiles", v)}
@@ -5348,6 +5385,19 @@ function StepAIBehavior({
   onAccessibilityFocusChange,
   performanceFocus,
   onPerformanceFocusChange,
+  mcpServers,
+  onMcpServersChange,
+  attemptWorkarounds,
+  onAttemptWorkaroundsChange,
+  serverAccess,
+  onServerAccessChange,
+  sshKeyPath,
+  onSshKeyPathChange,
+  manualDeployment,
+  onManualDeploymentChange,
+  deploymentMethod,
+  onDeploymentMethodChange,
+  hasCicd,
   importantFiles,
   importantFilesOther,
   onImportantFilesToggle,
@@ -5371,6 +5421,19 @@ function StepAIBehavior({
   onAccessibilityFocusChange: (v: boolean) => void;
   performanceFocus: boolean;
   onPerformanceFocusChange: (v: boolean) => void;
+  mcpServers: string;
+  onMcpServersChange: (v: string) => void;
+  attemptWorkarounds: boolean;
+  onAttemptWorkaroundsChange: (v: boolean) => void;
+  serverAccess: boolean;
+  onServerAccessChange: (v: boolean) => void;
+  sshKeyPath: string;
+  onSshKeyPathChange: (v: string) => void;
+  manualDeployment: boolean;
+  onManualDeploymentChange: (v: boolean) => void;
+  deploymentMethod: string;
+  onDeploymentMethodChange: (v: string) => void;
+  hasCicd: boolean;
   importantFiles: string[];
   importantFilesOther: string;
   onImportantFilesToggle: (v: string) => void;
@@ -5538,6 +5601,84 @@ function StepAIBehavior({
           checked={performanceFocus}
           onChange={onPerformanceFocusChange}
         />
+        
+      
+ + {/* MCP Servers */} +
+

πŸ”Œ MCP Servers

+

+ List any Model Context Protocol servers you have configured. The AI will know to use them when relevant. +

+ onMcpServersChange(e.target.value)} + placeholder="e.g. filesystem, github, postgres, docker" + className="mt-2 w-full rounded-md border bg-background px-3 py-2 text-sm" + /> +
+ + {/* Server Access */} +
+

πŸ–₯️ Infrastructure

+
+
+ onServerAccessChange(e.target.checked)} + className="h-4 w-4 rounded border-gray-300" + /> + +
+ {serverAccess && ( + onSshKeyPathChange(e.target.value)} + placeholder="SSH key path (leave empty for default ~/.ssh/)" + className="w-full rounded-md border bg-background px-3 py-2 text-sm" + /> + )} + {!hasCicd && ( + <> +
+ onManualDeploymentChange(e.target.checked)} + className="h-4 w-4 rounded border-gray-300" + /> + +
+ {manualDeployment && ( + + )} + + )} +
{/* Important Files to Read First */} diff --git a/src/lib/file-generator.ts b/src/lib/file-generator.ts index f6897135..cc66f8ff 100644 --- a/src/lib/file-generator.ts +++ b/src/lib/file-generator.ts @@ -115,6 +115,12 @@ interface WizardConfig { saveAllPreferences?: boolean; security?: SecurityConfig; useGitWorktrees?: boolean; // Use git worktrees for parallel AI agent sessions + mcpServers?: string; // Comma-separated list of MCP servers (e.g., "filesystem, github, postgres") + attemptWorkarounds?: boolean; // Whether AI should attempt workarounds when stuck + serverAccess?: boolean; // Whether project requires server login + sshKeyPath?: string; // Custom SSH key path (empty = default) + manualDeployment?: boolean; // Whether deployment is manual (no CI/CD) + deploymentMethod?: string; // portainer, docker_compose, kubernetes, bare_metal } // Security configuration (FREE tier) @@ -613,7 +619,7 @@ function generateCursorRules(config: WizardConfig, user: UserProfile): string { const authorName = user.displayName || user.name || "Developer"; lines.push(`- **Author**: ${bpVar(bp, "AUTHOR_NAME", authorName)}`); if (user.persona) { - lines.push(`- **Developer Type**: ${user.persona.replace(/_/g, " ")}`); + lines.push(`- **Developer Background**: ${user.persona.replace(/_/g, " ")} β€” adapt responses to this domain expertise`); } lines.push(`- **Experience Level**: ${user.skillLevel.charAt(0).toUpperCase() + user.skillLevel.slice(1)}`); lines.push(""); @@ -688,6 +694,48 @@ function generateCursorRules(config: WizardConfig, user: UserProfile): string { const ruleText = getRuleDescription(rule); if (ruleText) lines.push(`- ${ruleText}`); }); + if (config.attemptWorkarounds === true) { + lines.push("- When stuck, **attempt creative workarounds** before asking for help"); + } else if (config.attemptWorkarounds === false) { + lines.push("- When stuck, **stop and ask** rather than attempting workarounds"); + } + lines.push(""); + } + + // MCP Servers + if (config.mcpServers) { + const servers = config.mcpServers.split(",").map((s: string) => s.trim()).filter(Boolean); + if (servers.length > 0) { + lines.push("## MCP Servers"); + lines.push(""); + lines.push("The developer has these MCP (Model Context Protocol) servers available. Use them when relevant:"); + lines.push(""); + servers.forEach((server: string) => { + lines.push(`- ${server}`); + }); + lines.push(""); + } + } + + // Server access & deployment + if (config.serverAccess || config.manualDeployment) { + lines.push("## Infrastructure"); + lines.push(""); + if (config.serverAccess) { + const keyInfo = config.sshKeyPath + ? `SSH key: \`${config.sshKeyPath}\`` + : "SSH key in default location (~/.ssh/)"; + lines.push(`- **Server access**: via SSH. ${keyInfo}`); + } + if (config.manualDeployment && config.deploymentMethod) { + const methods: Record = { + portainer: "Portainer (GitOps stacks)", + docker_compose: "Docker Compose (manual)", + kubernetes: "Kubernetes (kubectl apply)", + bare_metal: "Bare metal (direct deployment)", + }; + lines.push(`- **Deployment**: ${methods[config.deploymentMethod] || config.deploymentMethod}`); + } lines.push(""); } @@ -1361,7 +1409,7 @@ function generateAgentsMd(config: WizardConfig, user: UserProfile): string { lines.push("- Be concise and direct"); } if (user.persona) { - lines.push(`- Developer context: ${user.persona.replace(/_/g, " ")}`); + lines.push(`- Developer background: ${user.persona.replace(/_/g, " ")} β€” adapt responses to this domain expertise`); } lines.push(`- Skill level: ${user.skillLevel.charAt(0).toUpperCase() + user.skillLevel.slice(1)}`); lines.push(""); @@ -1938,7 +1986,7 @@ function generateAiderConfig(config: WizardConfig, user: UserProfile): string { lines.push(`# Author: ${bpVar(bp, "AUTHOR_NAME", authorName)}`); lines.push(`# Skill Level: ${user.skillLevel.charAt(0).toUpperCase() + user.skillLevel.slice(1)}`); if (user.persona) { - lines.push(`# Developer Type: ${user.persona.replace(/_/g, " ")}`); + lines.push(`# Developer Background: ${user.persona.replace(/_/g, " ")} - adapt responses to this domain expertise`); } lines.push("#"); lines.push("# Communication Style:"); @@ -2737,7 +2785,7 @@ function generateTabnineConfig(config: WizardConfig, user: UserProfile): string lines.push(`# Author: ${authorName}`); lines.push(`# Skill Level: ${user.skillLevel.charAt(0).toUpperCase() + user.skillLevel.slice(1)}`); if (user.persona) { - lines.push(`# Developer Type: ${user.persona.replace(/_/g, " ")}`); + lines.push(`# Developer Background: ${user.persona.replace(/_/g, " ")} - adapt responses to this domain expertise`); } lines.push("#"); } @@ -3482,7 +3530,7 @@ function generateVoidConfig(config: WizardConfig, user: UserProfile): string { const authorName = user.displayName || user.name || "Developer"; rulesParts.push(`- **Author**: ${bpVar(bp, "AUTHOR_NAME", authorName)}`); if (user.persona) { - rulesParts.push(`- **Type**: ${user.persona.replace(/_/g, " ")}`); + rulesParts.push(`- **Developer Background**: ${user.persona.replace(/_/g, " ")} β€” adapt responses to this domain expertise`); } rulesParts.push(`- **Experience**: ${user.skillLevel.charAt(0).toUpperCase() + user.skillLevel.slice(1)}`); rulesParts.push(""); @@ -3947,6 +3995,12 @@ function getRuleDescription(ruleId: string): string { check_for_security_issues: "Review code for security vulnerabilities", document_complex_logic: "Add documentation for complex implementations", use_conventional_commits: "Follow conventional commit message format", + // Burke Holland-inspired rules + code_for_llms: "Optimize code for LLM reasoning: prefer flat/explicit patterns, minimal abstractions, structured logging, and linear control flow", + self_improving: "When you learn new project patterns or conventions, suggest updates to this configuration file", + verify_work: "Always verify your work before returning: run tests, check builds, confirm changes work as expected", + terminal_management: "Reuse existing terminals when possible. Close terminals you no longer need", + check_docs_first: "Always check documentation (via MCP or project docs) before assuming knowledge about APIs or libraries", }; return rules[ruleId] || ruleId; } From 58df4bb8ca4b6620e46099f72fc9ce8968fab7e4 Mon Sep 17 00:00:00 2001 From: GeiserX <9169332+GeiserX@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:55:43 +0100 Subject: [PATCH 2/4] fix: wrap paid template preview in JSX fragment for valid syntax --- src/app/blueprints/[id]/page.tsx | 98 ++++++++++++++++---------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/src/app/blueprints/[id]/page.tsx b/src/app/blueprints/[id]/page.tsx index f16f8f0e..dea95409 100644 --- a/src/app/blueprints/[id]/page.tsx +++ b/src/app/blueprints/[id]/page.tsx @@ -799,56 +799,58 @@ export default function BlueprintDetailPage() { {blueprint.isPaid && !blueprint.hasPurchased ? ( -
- {/* Blurred preview */} -
-                    {blueprint.preview}
-                  
- {/* Overlay */} -
- -

- Full content is locked. Purchase to access. -

- -
-
- {/* Show customizable variables even for locked templates */} - {blueprint.variables && blueprint.variables.length > 0 && ( -
-

- Customizable Variables ({blueprint.variables.length}) -

-

- This template includes variables you can customize after purchase: -

-
- {blueprint.variables.map((v: { name: string; defaultVal?: string }) => ( - - {v.name} - {v.defaultVal && ( - = {v.defaultVal} - )} - - ))} + <> +
+ {/* Blurred preview */} +
+                      {blueprint.preview}
+                    
+ {/* Overlay */} +
+ +

+ Full content is locked. Purchase to access. +

+
- )} + {/* Show customizable variables even for locked templates */} + {blueprint.variables && blueprint.variables.length > 0 && ( +
+

+ Customizable Variables ({blueprint.variables.length}) +

+

+ This template includes variables you can customize after purchase: +

+
+ {blueprint.variables.map((v: { name: string; defaultVal?: string }) => ( + + {v.name} + {v.defaultVal && ( + = {v.defaultVal} + )} + + ))} +
+
+ )} + ) : (

From 85d58085a27aac60a8ab2be126b780bf025d8afc Mon Sep 17 00:00:00 2001
From: GeiserX <9169332+GeiserX@users.noreply.github.com>
Date: Tue, 17 Feb 2026 13:58:04 +0100
Subject: [PATCH 3/4] fix: type blueprint.variables as union for paid template
 preview

---
 src/app/blueprints/[id]/page.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/app/blueprints/[id]/page.tsx b/src/app/blueprints/[id]/page.tsx
index dea95409..9fc37614 100644
--- a/src/app/blueprints/[id]/page.tsx
+++ b/src/app/blueprints/[id]/page.tsx
@@ -72,7 +72,7 @@ interface TemplateData {
   type?: string; // AGENTS_MD, CURSOR_COMMAND, CLAUDE_COMMAND, etc.
   targetPlatform?: string;
   compatibleWith?: string[];
-  variables?: Record;
+  variables?: Record | Array<{ name: string; defaultVal?: string }>;
   sensitiveFields?: Record<
     string,
     { label: string; required: boolean; placeholder?: string }
@@ -826,7 +826,7 @@ export default function BlueprintDetailPage() {
                     
{/* Show customizable variables even for locked templates */} - {blueprint.variables && blueprint.variables.length > 0 && ( + {Array.isArray(blueprint.variables) && blueprint.variables.length > 0 && (

Customizable Variables ({blueprint.variables.length}) @@ -835,7 +835,7 @@ export default function BlueprintDetailPage() { This template includes variables you can customize after purchase:

- {blueprint.variables.map((v: { name: string; defaultVal?: string }) => ( + {blueprint.variables.map((v) => ( Date: Tue, 17 Feb 2026 13:59:50 +0100 Subject: [PATCH 4/4] fix: filter array-type variables when passing to download modal --- src/app/blueprints/[id]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/blueprints/[id]/page.tsx b/src/app/blueprints/[id]/page.tsx index 9fc37614..e1ff82ff 100644 --- a/src/app/blueprints/[id]/page.tsx +++ b/src/app/blueprints/[id]/page.tsx @@ -918,7 +918,7 @@ export default function BlueprintDetailPage() { name: selectedVersion ? `${blueprint.name} (v${selectedVersion})` : blueprint.name, description: blueprint.description, content: selectedVersionContent || blueprint.content || "", - variables: blueprint.variables, + variables: Array.isArray(blueprint.variables) ? undefined : blueprint.variables, sensitiveFields: blueprint.sensitiveFields, targetPlatform: blueprint.targetPlatform || blueprint.type, compatibleWith: blueprint.compatibleWith,