lynxprompt 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -200,12 +200,7 @@ async function loginCommand() {
200
200
  pollSpinner.succeed("Authentication successful!");
201
201
  setToken(result.token);
202
202
  setUser(result.user);
203
- console.log();
204
- console.log(chalk.green(`\u2705 Logged in as ${chalk.bold(result.user.email)}`));
205
- console.log(chalk.gray(` Plan: ${result.user.plan}`));
206
- console.log(chalk.gray(` Token stored securely in config`));
207
- console.log();
208
- console.log(chalk.cyan("You're ready to use LynxPrompt CLI!"));
203
+ displayWelcome(result.user);
209
204
  return;
210
205
  }
211
206
  if (result.status === "expired") {
@@ -259,6 +254,53 @@ async function tryOpenBrowser(url) {
259
254
  function sleep(ms) {
260
255
  return new Promise((resolve) => setTimeout(resolve, ms));
261
256
  }
257
+ function displayWelcome(user) {
258
+ const plan = user.plan?.toUpperCase() || "FREE";
259
+ const name = user.name || user.email.split("@")[0];
260
+ const planConfig = {
261
+ FREE: { color: chalk.gray, emoji: "\u{1F193}", badge: "Free" },
262
+ PRO: { color: chalk.cyan, emoji: "\u26A1", badge: "Pro" },
263
+ MAX: { color: chalk.magenta, emoji: "\u{1F680}", badge: "Max" },
264
+ TEAMS: { color: chalk.yellow, emoji: "\u{1F465}", badge: "Teams" }
265
+ };
266
+ const config2 = planConfig[plan] || planConfig.FREE;
267
+ console.log();
268
+ console.log(chalk.bold("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
269
+ console.log(chalk.bold("\u2502") + " " + chalk.bold("\u2502"));
270
+ console.log(chalk.bold("\u2502") + chalk.green.bold(` ${config2.emoji} Welcome to LynxPrompt CLI!`) + " " + chalk.bold("\u2502"));
271
+ console.log(chalk.bold("\u2502") + " " + chalk.bold("\u2502"));
272
+ console.log(chalk.bold("\u2502") + ` ${chalk.white("User:")} ${chalk.bold(name.padEnd(38))}` + chalk.bold("\u2502"));
273
+ console.log(chalk.bold("\u2502") + ` ${chalk.white("Plan:")} ${config2.color(config2.badge.padEnd(38))}` + chalk.bold("\u2502"));
274
+ console.log(chalk.bold("\u2502") + " " + chalk.bold("\u2502"));
275
+ console.log(chalk.bold("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
276
+ console.log();
277
+ console.log(chalk.bold("\u{1F4CB} Your CLI Capabilities:"));
278
+ console.log();
279
+ console.log(chalk.green(" \u2713") + " " + chalk.white("lynxprompt init") + chalk.gray(" - Generate config files"));
280
+ console.log(chalk.green(" \u2713") + " " + chalk.white("lynxprompt wizard") + chalk.gray(" - Interactive wizard"));
281
+ console.log(chalk.green(" \u2713") + " " + chalk.white("lynxprompt list") + chalk.gray(" - List your blueprints"));
282
+ console.log(chalk.green(" \u2713") + " " + chalk.white("lynxprompt pull <id>") + chalk.gray(" - Download blueprints"));
283
+ console.log(chalk.green(" \u2713") + " " + chalk.white("lynxprompt push") + chalk.gray(" - Upload blueprints to marketplace"));
284
+ console.log(chalk.green(" \u2713") + " " + chalk.white("lynxprompt link") + chalk.gray(" - Link project to blueprint"));
285
+ console.log(chalk.green(" \u2713") + " " + chalk.white("lynxprompt sync") + chalk.gray(" - Sync linked blueprints"));
286
+ console.log(chalk.green(" \u2713") + " " + chalk.white("lynxprompt diff") + chalk.gray(" - Compare local vs remote"));
287
+ if (plan === "PRO" || plan === "MAX" || plan === "TEAMS") {
288
+ console.log();
289
+ console.log(chalk.cyan(" \u26A1") + " " + chalk.white("Advanced wizards") + chalk.gray(" - More customization options"));
290
+ console.log(chalk.cyan(" \u26A1") + " " + chalk.white("Sell blueprints") + chalk.gray(" - Monetize your configurations"));
291
+ }
292
+ if (plan === "MAX" || plan === "TEAMS") {
293
+ console.log(chalk.magenta(" \u{1F680}") + " " + chalk.white("All paid blueprints") + chalk.gray(" - Access premium content"));
294
+ console.log(chalk.magenta(" \u{1F680}") + " " + chalk.white("Priority support") + chalk.gray(" - Get help faster"));
295
+ }
296
+ if (plan === "TEAMS") {
297
+ console.log(chalk.yellow(" \u{1F465}") + " " + chalk.white("Team blueprints") + chalk.gray(" - Share with your team"));
298
+ console.log(chalk.yellow(" \u{1F465}") + " " + chalk.white("SSO integration") + chalk.gray(" - Enterprise authentication"));
299
+ }
300
+ console.log();
301
+ console.log(chalk.gray("Token stored securely. Run ") + chalk.cyan("lynxprompt --help") + chalk.gray(" to see all commands."));
302
+ console.log();
303
+ }
262
304
 
263
305
  // src/commands/logout.ts
264
306
  import chalk2 from "chalk";
@@ -1370,7 +1412,7 @@ function formatDetectionResults(result) {
1370
1412
  }
1371
1413
 
1372
1414
  // src/utils/detect.ts
1373
- import { readFile as readFile3, access as access3 } from "fs/promises";
1415
+ import { readFile as readFile3, access as access2 } from "fs/promises";
1374
1416
  import { join as join4 } from "path";
1375
1417
  var JS_FRAMEWORK_PATTERNS = {
1376
1418
  nextjs: ["next"],
@@ -1593,7 +1635,7 @@ async function detectProject(cwd) {
1593
1635
  }
1594
1636
  async function fileExists(path2) {
1595
1637
  try {
1596
- await access3(path2);
1638
+ await access2(path2);
1597
1639
  return true;
1598
1640
  } catch {
1599
1641
  return false;
@@ -1964,7 +2006,7 @@ async function initCommand(options) {
1964
2006
  import chalk8 from "chalk";
1965
2007
  import prompts4 from "prompts";
1966
2008
  import ora7 from "ora";
1967
- import { writeFile as writeFile4, mkdir as mkdir4, access as access5 } from "fs/promises";
2009
+ import { writeFile as writeFile4, mkdir as mkdir4, access as access3 } from "fs/promises";
1968
2010
  import { join as join6, dirname as dirname4 } from "path";
1969
2011
 
1970
2012
  // src/utils/generator.ts
@@ -1974,7 +2016,17 @@ var PLATFORM_FILES = {
1974
2016
  claude: "CLAUDE.md",
1975
2017
  copilot: ".github/copilot-instructions.md",
1976
2018
  windsurf: ".windsurfrules",
1977
- zed: ".zed/instructions.md"
2019
+ zed: ".zed/instructions.md",
2020
+ aider: ".aider.conf.yml",
2021
+ cline: ".clinerules",
2022
+ continue: ".continue/rules.md",
2023
+ cody: ".cody/rules.md",
2024
+ amazonq: ".amazonq/rules/project.md",
2025
+ tabnine: ".tabnine.yaml",
2026
+ supermaven: ".supermaven/rules.md",
2027
+ codegpt: ".codegpt/rules.md",
2028
+ void: ".void/rules.md",
2029
+ goose: ".goosehints"
1978
2030
  };
1979
2031
  var PERSONA_DESCRIPTIONS = {
1980
2032
  backend: "a senior backend developer specializing in APIs, databases, and microservices architecture",
@@ -1995,6 +2047,8 @@ var STACK_NAMES = {
1995
2047
  ruby: "Ruby",
1996
2048
  php: "PHP",
1997
2049
  swift: "Swift",
2050
+ kotlin: "Kotlin",
2051
+ cpp: "C/C++",
1998
2052
  react: "React",
1999
2053
  nextjs: "Next.js",
2000
2054
  vue: "Vue.js",
@@ -2007,10 +2061,44 @@ var STACK_NAMES = {
2007
2061
  spring: "Spring Boot",
2008
2062
  rails: "Ruby on Rails",
2009
2063
  laravel: "Laravel",
2064
+ nestjs: "NestJS",
2065
+ vite: "Vite",
2066
+ "react-native": "React Native",
2067
+ postgresql: "PostgreSQL",
2068
+ mysql: "MySQL",
2069
+ mongodb: "MongoDB",
2070
+ redis: "Redis",
2071
+ sqlite: "SQLite",
2072
+ supabase: "Supabase",
2073
+ firebase: "Firebase",
2010
2074
  prisma: "Prisma",
2011
2075
  tailwind: "Tailwind CSS",
2012
2076
  fastify: "Fastify"
2013
2077
  };
2078
+ var NAMING_DESCRIPTIONS = {
2079
+ language_default: "follow idiomatic conventions for the primary language",
2080
+ camelCase: "use camelCase for variables and functions",
2081
+ snake_case: "use snake_case for variables and functions",
2082
+ PascalCase: "use PascalCase for classes and types",
2083
+ "kebab-case": "use kebab-case for file names and CSS classes"
2084
+ };
2085
+ var AI_BEHAVIOR_DESCRIPTIONS = {
2086
+ explain_changes: "Always explain what changes you're making and why before implementing them",
2087
+ preserve_style: "Preserve and follow the existing code style in the project",
2088
+ minimal_changes: "Make minimal, focused changes - avoid unnecessary refactoring",
2089
+ no_comments: "Avoid adding unnecessary comments; code should be self-documenting",
2090
+ prefer_simple: "Prefer simpler solutions over clever ones",
2091
+ test_first: "Write tests before implementing new functionality (TDD)",
2092
+ no_console: "Remove console.log/print statements before committing",
2093
+ type_strict: "Be strict with types - avoid any/Any/Object types"
2094
+ };
2095
+ var IMPORTANT_FILES_PATHS = {
2096
+ readme: "README.md",
2097
+ package: "package.json or pyproject.toml",
2098
+ tsconfig: "tsconfig.json or similar config",
2099
+ architecture: "ARCHITECTURE.md",
2100
+ contributing: "CONTRIBUTING.md"
2101
+ };
2014
2102
  var BOUNDARIES = {
2015
2103
  conservative: {
2016
2104
  always: ["Read any file in the project", "Run lint and format commands"],
@@ -2069,6 +2157,12 @@ var BOUNDARIES = {
2069
2157
  ]
2070
2158
  }
2071
2159
  };
2160
+ var TEST_LEVEL_DESCRIPTIONS = {
2161
+ smoke: "Quick sanity checks for critical paths",
2162
+ unit: "Unit tests for individual functions and components",
2163
+ integration: "Integration tests for component interactions",
2164
+ e2e: "End-to-end tests for full user flows"
2165
+ };
2072
2166
  function generateConfig(options) {
2073
2167
  const files = {};
2074
2168
  for (const platform of options.platforms) {
@@ -2082,8 +2176,12 @@ function generateConfig(options) {
2082
2176
  function generateFileContent(options, platform) {
2083
2177
  const sections = [];
2084
2178
  const isMdc = platform === "cursor";
2085
- const isPlainText = platform === "windsurf";
2086
- const isMarkdown = !isMdc && !isPlainText;
2179
+ const isYaml = platform === "aider" || platform === "tabnine";
2180
+ const isPlainText = platform === "windsurf" || platform === "cline" || platform === "goose";
2181
+ const isMarkdown = !isMdc && !isYaml && !isPlainText;
2182
+ if (isYaml) {
2183
+ return generateYamlConfig(options, platform);
2184
+ }
2087
2185
  if (isMdc) {
2088
2186
  sections.push("---");
2089
2187
  sections.push(`description: "${options.name} - AI coding rules"`);
@@ -2098,6 +2196,23 @@ function generateFileContent(options, platform) {
2098
2196
  sections.push(`# ${options.name} - AI Assistant Configuration`);
2099
2197
  sections.push("");
2100
2198
  }
2199
+ if (options.projectType) {
2200
+ const typeContexts = {
2201
+ work: "This is a professional/enterprise project. Follow strict procedures and maintain high code quality.",
2202
+ leisure: "This is a personal/hobby project. Feel free to be more experimental and creative.",
2203
+ opensource: "This is an open-source project. Consider community guidelines and contribution standards.",
2204
+ learning: "This is an educational project. Explain concepts and be patient with learning-focused approaches."
2205
+ };
2206
+ if (typeContexts[options.projectType]) {
2207
+ if (isMarkdown || isMdc) {
2208
+ sections.push(`> **Project Context:** ${typeContexts[options.projectType]}`);
2209
+ sections.push("");
2210
+ } else {
2211
+ sections.push(`Project Context: ${typeContexts[options.projectType]}`);
2212
+ sections.push("");
2213
+ }
2214
+ }
2215
+ }
2101
2216
  const personaDesc = PERSONA_DESCRIPTIONS[options.persona] || options.persona;
2102
2217
  if (isMarkdown || isMdc) {
2103
2218
  sections.push("## Persona");
@@ -2128,7 +2243,25 @@ function generateFileContent(options, platform) {
2128
2243
  }
2129
2244
  sections.push("");
2130
2245
  }
2131
- const hasCommands = Object.values(options.commands).some(Boolean);
2246
+ if (options.repoHost || options.license || options.conventionalCommits) {
2247
+ if (isMarkdown || isMdc) {
2248
+ sections.push("## Repository");
2249
+ sections.push("");
2250
+ if (options.repoHost) {
2251
+ sections.push(`- **Host:** ${options.repoHost.charAt(0).toUpperCase() + options.repoHost.slice(1)}`);
2252
+ }
2253
+ if (options.license && options.license !== "none") {
2254
+ sections.push(`- **License:** ${options.license.toUpperCase()}`);
2255
+ }
2256
+ if (options.conventionalCommits) {
2257
+ sections.push("- **Commits:** Follow [Conventional Commits](https://conventionalcommits.org) format");
2258
+ }
2259
+ sections.push("");
2260
+ }
2261
+ }
2262
+ const hasCommands = options.commands && Object.values(options.commands).some(
2263
+ (v) => Array.isArray(v) ? v.length > 0 : Boolean(v)
2264
+ );
2132
2265
  if (hasCommands) {
2133
2266
  if (isMarkdown || isMdc) {
2134
2267
  sections.push("## Commands");
@@ -2139,24 +2272,68 @@ function generateFileContent(options, platform) {
2139
2272
  } else {
2140
2273
  sections.push("Commands:");
2141
2274
  }
2142
- if (options.commands.build) {
2143
- sections.push(isMarkdown || isMdc ? `# Build: ${options.commands.build}` : `- Build: ${options.commands.build}`);
2275
+ const cmdCategories = ["build", "test", "lint", "dev", "custom"];
2276
+ for (const cat of cmdCategories) {
2277
+ const cmd = options.commands[cat];
2278
+ if (cmd) {
2279
+ const cmds = Array.isArray(cmd) ? cmd : [cmd];
2280
+ for (const c of cmds) {
2281
+ if (c) {
2282
+ const label = cat.charAt(0).toUpperCase() + cat.slice(1);
2283
+ sections.push(isMarkdown || isMdc ? `# ${label}: ${c}` : `- ${label}: ${c}`);
2284
+ }
2285
+ }
2286
+ }
2144
2287
  }
2145
- if (options.commands.test) {
2146
- sections.push(isMarkdown || isMdc ? `# Test: ${options.commands.test}` : `- Test: ${options.commands.test}`);
2288
+ if (isMarkdown || isMdc) {
2289
+ sections.push("```");
2147
2290
  }
2148
- if (options.commands.lint) {
2149
- sections.push(isMarkdown || isMdc ? `# Lint: ${options.commands.lint}` : `- Lint: ${options.commands.lint}`);
2291
+ sections.push("");
2292
+ }
2293
+ if (options.aiBehavior && options.aiBehavior.length > 0) {
2294
+ if (isMarkdown || isMdc) {
2295
+ sections.push("## AI Behavior Rules");
2296
+ sections.push("");
2297
+ for (const rule of options.aiBehavior) {
2298
+ const desc = AI_BEHAVIOR_DESCRIPTIONS[rule];
2299
+ if (desc) {
2300
+ sections.push(`- ${desc}`);
2301
+ }
2302
+ }
2303
+ sections.push("");
2150
2304
  }
2151
- if (options.commands.dev) {
2152
- sections.push(isMarkdown || isMdc ? `# Dev: ${options.commands.dev}` : `- Dev: ${options.commands.dev}`);
2305
+ }
2306
+ if (options.importantFiles && options.importantFiles.length > 0) {
2307
+ if (isMarkdown || isMdc) {
2308
+ sections.push("## Important Files to Read");
2309
+ sections.push("");
2310
+ sections.push("Always read these files first to understand the project context:");
2311
+ sections.push("");
2312
+ for (const file of options.importantFiles) {
2313
+ const path2 = IMPORTANT_FILES_PATHS[file];
2314
+ if (path2) {
2315
+ sections.push(`- \`${path2}\``);
2316
+ }
2317
+ }
2318
+ sections.push("");
2153
2319
  }
2320
+ }
2321
+ if (options.selfImprove) {
2154
2322
  if (isMarkdown || isMdc) {
2155
- sections.push("```");
2323
+ sections.push("## Self-Improving Blueprint");
2324
+ sections.push("");
2325
+ sections.push("> **Auto-update enabled:** As you work on this project, track patterns and update this configuration file to better reflect the project's conventions and preferences.");
2326
+ sections.push("");
2156
2327
  }
2157
- sections.push("");
2158
2328
  }
2159
- const boundaries = BOUNDARIES[options.boundaries];
2329
+ let boundaries = BOUNDARIES[options.boundaries];
2330
+ if (options.boundaryNever?.length || options.boundaryAsk?.length) {
2331
+ boundaries = {
2332
+ ...boundaries,
2333
+ never: options.boundaryNever?.length ? options.boundaryNever : boundaries.never,
2334
+ askFirst: options.boundaryAsk?.length ? options.boundaryAsk : boundaries.askFirst
2335
+ };
2336
+ }
2160
2337
  if (boundaries) {
2161
2338
  if (isMarkdown || isMdc) {
2162
2339
  sections.push("## Boundaries");
@@ -2201,6 +2378,27 @@ function generateFileContent(options, platform) {
2201
2378
  if (isMarkdown || isMdc) {
2202
2379
  sections.push("## Code Style");
2203
2380
  sections.push("");
2381
+ if (options.namingConvention) {
2382
+ const namingDesc = NAMING_DESCRIPTIONS[options.namingConvention];
2383
+ if (namingDesc) {
2384
+ sections.push(`- **Naming:** ${namingDesc}`);
2385
+ }
2386
+ }
2387
+ if (options.errorHandling) {
2388
+ const errorStyles = {
2389
+ try_catch: "Use try/catch blocks for error handling",
2390
+ result_types: "Use Result/Either types for error handling",
2391
+ error_codes: "Use error codes with proper documentation",
2392
+ exceptions: "Use custom exception classes"
2393
+ };
2394
+ if (errorStyles[options.errorHandling]) {
2395
+ sections.push(`- **Errors:** ${errorStyles[options.errorHandling]}`);
2396
+ }
2397
+ }
2398
+ if (options.styleNotes) {
2399
+ sections.push(`- **Notes:** ${options.styleNotes}`);
2400
+ }
2401
+ sections.push("");
2204
2402
  sections.push("Follow these conventions:");
2205
2403
  sections.push("");
2206
2404
  if (options.stack.includes("typescript") || options.stack.includes("javascript")) {
@@ -2234,6 +2432,49 @@ function generateFileContent(options, platform) {
2234
2432
  sections.push("- Keep functions focused and testable");
2235
2433
  sections.push("");
2236
2434
  }
2435
+ if (options.testLevels?.length || options.testFrameworks?.length || options.coverageTarget) {
2436
+ if (isMarkdown || isMdc) {
2437
+ sections.push("## Testing Strategy");
2438
+ sections.push("");
2439
+ if (options.testLevels?.length) {
2440
+ sections.push("### Test Levels");
2441
+ sections.push("");
2442
+ for (const level of options.testLevels) {
2443
+ const desc = TEST_LEVEL_DESCRIPTIONS[level];
2444
+ if (desc) {
2445
+ sections.push(`- **${level.charAt(0).toUpperCase() + level.slice(1)}:** ${desc}`);
2446
+ }
2447
+ }
2448
+ sections.push("");
2449
+ }
2450
+ if (options.testFrameworks?.length) {
2451
+ sections.push("### Frameworks");
2452
+ sections.push("");
2453
+ sections.push(`Use: ${options.testFrameworks.join(", ")}`);
2454
+ sections.push("");
2455
+ }
2456
+ if (options.coverageTarget) {
2457
+ sections.push(`### Coverage Target: ${options.coverageTarget}%`);
2458
+ sections.push("");
2459
+ }
2460
+ if (options.testNotes) {
2461
+ sections.push(`**Notes:** ${options.testNotes}`);
2462
+ sections.push("");
2463
+ }
2464
+ }
2465
+ }
2466
+ if (options.extraNotes) {
2467
+ if (isMarkdown || isMdc) {
2468
+ sections.push("## Additional Notes");
2469
+ sections.push("");
2470
+ sections.push(options.extraNotes);
2471
+ sections.push("");
2472
+ } else {
2473
+ sections.push("Additional Notes:");
2474
+ sections.push(options.extraNotes);
2475
+ sections.push("");
2476
+ }
2477
+ }
2237
2478
  if (isMarkdown || isMdc) {
2238
2479
  sections.push("---");
2239
2480
  sections.push("");
@@ -2241,105 +2482,446 @@ function generateFileContent(options, platform) {
2241
2482
  }
2242
2483
  return sections.join("\n");
2243
2484
  }
2485
+ function generateYamlConfig(options, platform) {
2486
+ const lines = [];
2487
+ if (platform === "aider") {
2488
+ lines.push("# Aider configuration");
2489
+ lines.push(`# Project: ${options.name}`);
2490
+ lines.push("");
2491
+ lines.push("# Model settings");
2492
+ lines.push("model: gpt-4");
2493
+ lines.push("");
2494
+ lines.push("# Code style");
2495
+ if (options.stack.includes("typescript") || options.stack.includes("javascript")) {
2496
+ lines.push("auto-lint: true");
2497
+ }
2498
+ lines.push("");
2499
+ lines.push("# Custom instructions");
2500
+ lines.push("read:");
2501
+ lines.push(" - README.md");
2502
+ if (options.importantFiles?.includes("architecture")) {
2503
+ lines.push(" - ARCHITECTURE.md");
2504
+ }
2505
+ } else if (platform === "tabnine") {
2506
+ lines.push("# Tabnine configuration");
2507
+ lines.push(`# Project: ${options.name}`);
2508
+ lines.push("");
2509
+ lines.push("version: 1.0.0");
2510
+ lines.push("");
2511
+ lines.push("project:");
2512
+ lines.push(` name: ${options.name}`);
2513
+ if (options.description) {
2514
+ lines.push(` description: "${options.description}"`);
2515
+ }
2516
+ lines.push("");
2517
+ lines.push("context:");
2518
+ lines.push(" include:");
2519
+ lines.push(' - "**/*.ts"');
2520
+ lines.push(' - "**/*.js"');
2521
+ lines.push(' - "**/*.py"');
2522
+ }
2523
+ lines.push("");
2524
+ lines.push(`# Generated by LynxPrompt CLI`);
2525
+ return lines.join("\n");
2526
+ }
2244
2527
 
2245
2528
  // src/commands/wizard.ts
2529
+ var WIZARD_STEPS = [
2530
+ { id: "format", title: "Output Format", icon: "\u{1F4E4}", tier: "basic" },
2531
+ { id: "project", title: "Project Basics", icon: "\u2728", tier: "basic" },
2532
+ { id: "tech", title: "Tech Stack", icon: "\u{1F4BB}", tier: "basic" },
2533
+ { id: "repo", title: "Repository Setup", icon: "\u{1F500}", tier: "basic" },
2534
+ { id: "commands", title: "Commands", icon: "\u{1F4CB}", tier: "intermediate" },
2535
+ { id: "code_style", title: "Code Style", icon: "\u{1FA84}", tier: "intermediate" },
2536
+ { id: "ai", title: "AI Behavior", icon: "\u{1F9E0}", tier: "basic" },
2537
+ { id: "boundaries", title: "Boundaries", icon: "\u{1F6E1}\uFE0F", tier: "advanced" },
2538
+ { id: "testing", title: "Testing Strategy", icon: "\u{1F9EA}", tier: "advanced" },
2539
+ { id: "static", title: "Static Files", icon: "\u{1F4C4}", tier: "advanced" },
2540
+ { id: "extra", title: "Final Details", icon: "\u{1F4AC}", tier: "basic" }
2541
+ ];
2542
+ var ALL_PLATFORMS = [
2543
+ { id: "agents", name: "Universal (AGENTS.md)", file: "AGENTS.md", icon: "\u{1F310}", note: "Works with all AI-enabled IDEs" },
2544
+ { id: "cursor", name: "Cursor", file: ".cursor/rules/", icon: "\u26A1", note: "Native project rules format" },
2545
+ { id: "claude", name: "Claude Code", file: "CLAUDE.md", icon: "\u{1F9E0}", note: "Also works with Cursor" },
2546
+ { id: "copilot", name: "GitHub Copilot", file: ".github/copilot-instructions.md", icon: "\u{1F419}", note: "VS Code & JetBrains" },
2547
+ { id: "windsurf", name: "Windsurf", file: ".windsurfrules", icon: "\u{1F3C4}", note: "Codeium IDE" },
2548
+ { id: "zed", name: "Zed", file: ".zed/instructions.md", icon: "\u26A1", note: "Zed editor" },
2549
+ { id: "aider", name: "Aider", file: ".aider.conf.yml", icon: "\u{1F916}", note: "CLI AI pair programming" },
2550
+ { id: "cline", name: "Cline", file: ".clinerules", icon: "\u{1F527}", note: "VS Code extension" },
2551
+ { id: "continue", name: "Continue", file: ".continue/config.json", icon: "\u27A1\uFE0F", note: "Open-source autopilot" },
2552
+ { id: "cody", name: "Sourcegraph Cody", file: ".cody/config.json", icon: "\u{1F50D}", note: "Context-aware AI" },
2553
+ { id: "amazonq", name: "Amazon Q", file: ".amazonq/rules/", icon: "\u{1F4E6}", note: "AWS AI assistant" },
2554
+ { id: "tabnine", name: "Tabnine", file: ".tabnine.yaml", icon: "\u{1F4DD}", note: "AI code completion" },
2555
+ { id: "supermaven", name: "Supermaven", file: ".supermaven/config.json", icon: "\u{1F9B8}", note: "Fast AI completions" },
2556
+ { id: "codegpt", name: "CodeGPT", file: ".codegpt/config.json", icon: "\u{1F4AC}", note: "VS Code AI assistant" },
2557
+ { id: "void", name: "Void", file: ".void/config.json", icon: "\u{1F573}\uFE0F", note: "Open-source Cursor alt" },
2558
+ { id: "goose", name: "Goose", file: ".goosehints", icon: "\u{1FABF}", note: "Block AI agent" }
2559
+ ];
2246
2560
  var OUTPUT_FORMATS = [
2247
- {
2248
- title: "AGENTS.md (Universal)",
2249
- value: "agents",
2250
- description: "Works with Claude Code, GitHub Copilot, Aider, and most AI editors",
2251
- recommended: true
2252
- },
2253
- {
2254
- title: "Cursor (.cursor/rules/)",
2255
- value: "cursor",
2256
- description: "Cursor IDE with MDC format"
2257
- },
2258
- {
2259
- title: "Multiple formats",
2260
- value: "multiple",
2261
- description: "Select multiple AI editors to generate for"
2262
- }
2561
+ { title: "\u{1F310} AGENTS.md (Universal)", value: "agents", description: "Works with Claude, Copilot, Aider, Devin & more", recommended: true },
2562
+ { title: "\u26A1 Cursor", value: "cursor", description: ".cursor/rules/ native format" },
2563
+ { title: "\u{1F9E0} Claude Code", value: "claude", description: "CLAUDE.md format" },
2564
+ { title: "\u{1F419} GitHub Copilot", value: "copilot", description: ".github/copilot-instructions.md" },
2565
+ { title: "\u{1F3C4} Windsurf", value: "windsurf", description: ".windsurfrules configuration" },
2566
+ { title: "\u{1F4E6} Multiple platforms...", value: "multiple", description: "Select from 16+ supported AI editors" }
2263
2567
  ];
2264
- var TECH_STACKS = [
2265
- { title: "TypeScript", value: "typescript" },
2266
- { title: "JavaScript", value: "javascript" },
2267
- { title: "Python", value: "python" },
2268
- { title: "Go", value: "go" },
2269
- { title: "Rust", value: "rust" },
2270
- { title: "Java", value: "java" },
2271
- { title: "C#/.NET", value: "csharp" },
2272
- { title: "Ruby", value: "ruby" },
2273
- { title: "PHP", value: "php" },
2274
- { title: "Swift", value: "swift" }
2568
+ var LANGUAGES = [
2569
+ { title: "\u{1F537} TypeScript", value: "typescript" },
2570
+ { title: "\u{1F7E1} JavaScript", value: "javascript" },
2571
+ { title: "\u{1F40D} Python", value: "python" },
2572
+ { title: "\u{1F535} Go", value: "go" },
2573
+ { title: "\u{1F980} Rust", value: "rust" },
2574
+ { title: "\u2615 Java", value: "java" },
2575
+ { title: "\u{1F49C} C#/.NET", value: "csharp" },
2576
+ { title: "\u{1F48E} Ruby", value: "ruby" },
2577
+ { title: "\u{1F418} PHP", value: "php" },
2578
+ { title: "\u{1F34E} Swift", value: "swift" },
2579
+ { title: "\u{1F536} Kotlin", value: "kotlin" },
2580
+ { title: "\u2B1B C/C++", value: "cpp" }
2275
2581
  ];
2276
2582
  var FRAMEWORKS = [
2277
- { title: "React", value: "react" },
2278
- { title: "Next.js", value: "nextjs" },
2279
- { title: "Vue.js", value: "vue" },
2280
- { title: "Angular", value: "angular" },
2281
- { title: "Svelte", value: "svelte" },
2282
- { title: "Express", value: "express" },
2283
- { title: "FastAPI", value: "fastapi" },
2284
- { title: "Django", value: "django" },
2285
- { title: "Flask", value: "flask" },
2286
- { title: "Spring Boot", value: "spring" },
2287
- { title: "Rails", value: "rails" },
2288
- { title: "Laravel", value: "laravel" }
2583
+ { title: "\u269B\uFE0F React", value: "react" },
2584
+ { title: "\u25B2 Next.js", value: "nextjs" },
2585
+ { title: "\u{1F49A} Vue.js", value: "vue" },
2586
+ { title: "\u{1F170}\uFE0F Angular", value: "angular" },
2587
+ { title: "\u{1F525} Svelte", value: "svelte" },
2588
+ { title: "\u{1F682} Express", value: "express" },
2589
+ { title: "\u26A1 FastAPI", value: "fastapi" },
2590
+ { title: "\u{1F3B8} Django", value: "django" },
2591
+ { title: "\u{1F9EA} Flask", value: "flask" },
2592
+ { title: "\u{1F343} Spring", value: "spring" },
2593
+ { title: "\u{1F48E} Rails", value: "rails" },
2594
+ { title: "\u{1F534} Laravel", value: "laravel" },
2595
+ { title: "\u{1F3D7}\uFE0F NestJS", value: "nestjs" },
2596
+ { title: "\u26A1 Vite", value: "vite" },
2597
+ { title: "\u{1F4F1} React Native", value: "react-native" }
2598
+ ];
2599
+ var DATABASES = [
2600
+ { title: "\u{1F418} PostgreSQL", value: "postgresql" },
2601
+ { title: "\u{1F42C} MySQL", value: "mysql" },
2602
+ { title: "\u{1F343} MongoDB", value: "mongodb" },
2603
+ { title: "\u{1F534} Redis", value: "redis" },
2604
+ { title: "\u{1F4CA} SQLite", value: "sqlite" },
2605
+ { title: "\u2601\uFE0F Supabase", value: "supabase" },
2606
+ { title: "\u{1F525} Firebase", value: "firebase" },
2607
+ { title: "\u{1F4C2} Prisma", value: "prisma" }
2289
2608
  ];
2290
- var PLATFORMS = [
2291
- { title: "AGENTS.md (Universal)", value: "agents", filename: "AGENTS.md" },
2292
- { title: "Cursor (.cursor/rules/)", value: "cursor", filename: ".cursor/rules/project.mdc" },
2293
- { title: "Claude Code (CLAUDE.md)", value: "claude", filename: "CLAUDE.md" },
2294
- { title: "GitHub Copilot", value: "copilot", filename: ".github/copilot-instructions.md" },
2295
- { title: "Windsurf (.windsurfrules)", value: "windsurf", filename: ".windsurfrules" },
2296
- { title: "Zed", value: "zed", filename: ".zed/instructions.md" }
2609
+ var REPO_HOSTS = [
2610
+ { id: "github", label: "GitHub", icon: "\u{1F419}" },
2611
+ { id: "gitlab", label: "GitLab", icon: "\u{1F98A}" },
2612
+ { id: "bitbucket", label: "Bitbucket", icon: "\u{1FAA3}" },
2613
+ { id: "gitea", label: "Gitea", icon: "\u{1F375}" },
2614
+ { id: "azure", label: "Azure DevOps", icon: "\u2601\uFE0F" },
2615
+ { id: "other", label: "Other", icon: "\u{1F4E6}" }
2616
+ ];
2617
+ var LICENSES = [
2618
+ { id: "mit", label: "MIT" },
2619
+ { id: "apache-2.0", label: "Apache 2.0" },
2620
+ { id: "gpl-3.0", label: "GPL 3.0" },
2621
+ { id: "lgpl-3.0", label: "LGPL 3.0" },
2622
+ { id: "agpl-3.0", label: "AGPL 3.0" },
2623
+ { id: "bsd-3", label: "BSD 3-Clause" },
2624
+ { id: "mpl-2.0", label: "MPL 2.0" },
2625
+ { id: "unlicense", label: "Unlicense" },
2626
+ { id: "none", label: "None / Proprietary" }
2627
+ ];
2628
+ var COMMON_COMMANDS = {
2629
+ build: [
2630
+ "npm run build",
2631
+ "pnpm build",
2632
+ "yarn build",
2633
+ "bun run build",
2634
+ "next build",
2635
+ "vite build",
2636
+ "tsc",
2637
+ "tsc --noEmit",
2638
+ "go build",
2639
+ "cargo build",
2640
+ "cargo build --release",
2641
+ "mvn package",
2642
+ "gradle build",
2643
+ "dotnet build",
2644
+ "docker build -t app .",
2645
+ "docker compose build"
2646
+ ],
2647
+ test: [
2648
+ "npm test",
2649
+ "pnpm test",
2650
+ "yarn test",
2651
+ "bun test",
2652
+ "vitest",
2653
+ "vitest run",
2654
+ "jest",
2655
+ "jest --coverage",
2656
+ "pytest",
2657
+ "pytest --cov",
2658
+ "go test ./...",
2659
+ "cargo test",
2660
+ "mvn test",
2661
+ "gradle test",
2662
+ "playwright test",
2663
+ "cypress run"
2664
+ ],
2665
+ lint: [
2666
+ "npm run lint",
2667
+ "pnpm lint",
2668
+ "eslint .",
2669
+ "eslint . --fix",
2670
+ "prettier --check .",
2671
+ "prettier --write .",
2672
+ "ruff check",
2673
+ "ruff format",
2674
+ "black .",
2675
+ "flake8",
2676
+ "golangci-lint run",
2677
+ "cargo clippy",
2678
+ "rubocop"
2679
+ ],
2680
+ dev: [
2681
+ "npm run dev",
2682
+ "pnpm dev",
2683
+ "yarn dev",
2684
+ "bun run dev",
2685
+ "next dev",
2686
+ "vite",
2687
+ "vite dev",
2688
+ "uvicorn main:app --reload",
2689
+ "flask run",
2690
+ "rails server",
2691
+ "go run .",
2692
+ "cargo run",
2693
+ "dotnet run"
2694
+ ]
2695
+ };
2696
+ var NAMING_CONVENTIONS = [
2697
+ { id: "language_default", label: "Follow language conventions", desc: "Use idiomatic style" },
2698
+ { id: "camelCase", label: "camelCase", desc: "JavaScript, TypeScript, Java" },
2699
+ { id: "snake_case", label: "snake_case", desc: "Python, Ruby, Rust, Go" },
2700
+ { id: "PascalCase", label: "PascalCase", desc: "C#, .NET classes" },
2701
+ { id: "kebab-case", label: "kebab-case", desc: "CSS, HTML, URLs" }
2297
2702
  ];
2298
- var PERSONAS = [
2299
- { title: "Full-Stack Developer - Complete application setups", value: "fullstack" },
2300
- { title: "Backend Developer - APIs, databases, microservices", value: "backend" },
2301
- { title: "Frontend Developer - UI, components, styling", value: "frontend" },
2302
- { title: "DevOps Engineer - Infrastructure, CI/CD, containers", value: "devops" },
2303
- { title: "Data Engineer - Pipelines, ETL, databases", value: "data" },
2304
- { title: "Security Engineer - Secure code, vulnerabilities", value: "security" },
2305
- { title: "Custom...", value: "custom" }
2703
+ var ERROR_PATTERNS = [
2704
+ { id: "try_catch", label: "try/catch blocks" },
2705
+ { id: "result_types", label: "Result/Either types" },
2706
+ { id: "error_codes", label: "Error codes" },
2707
+ { id: "exceptions", label: "Custom exceptions" },
2708
+ { id: "other", label: "Other" }
2709
+ ];
2710
+ var AI_BEHAVIOR_RULES = [
2711
+ { id: "explain_changes", label: "Explain changes before making them", recommended: true },
2712
+ { id: "preserve_style", label: "Preserve existing code style", recommended: true },
2713
+ { id: "minimal_changes", label: "Make minimal, focused changes", recommended: true },
2714
+ { id: "no_comments", label: "Avoid adding unnecessary comments", recommended: false },
2715
+ { id: "prefer_simple", label: "Prefer simpler solutions", recommended: true },
2716
+ { id: "test_first", label: "Write tests before implementation", recommended: false },
2717
+ { id: "no_console", label: "Remove console.log/print before committing", recommended: false },
2718
+ { id: "type_strict", label: "Be strict with types (no any/Any)", recommended: false }
2719
+ ];
2720
+ var IMPORTANT_FILES = [
2721
+ { id: "readme", label: "README.md", icon: "\u{1F4D6}" },
2722
+ { id: "package", label: "package.json / pyproject.toml", icon: "\u{1F4E6}" },
2723
+ { id: "tsconfig", label: "tsconfig.json / config files", icon: "\u2699\uFE0F" },
2724
+ { id: "architecture", label: "ARCHITECTURE.md", icon: "\u{1F3D7}\uFE0F" },
2725
+ { id: "contributing", label: "CONTRIBUTING.md", icon: "\u{1F91D}" }
2306
2726
  ];
2307
2727
  var BOUNDARY_PRESETS = [
2308
2728
  {
2309
- title: "Standard - Balance of freedom and safety (recommended)",
2729
+ title: "\u{1F7E2} Standard",
2310
2730
  value: "standard",
2731
+ description: "Balanced freedom & safety",
2311
2732
  always: ["Read any file", "Modify files in src/", "Run build/test/lint", "Create test files"],
2312
2733
  askFirst: ["Add new dependencies", "Modify config files", "Create new modules"],
2313
2734
  never: ["Delete production data", "Modify .env secrets", "Force push"]
2314
2735
  },
2315
2736
  {
2316
- title: "Conservative - Ask before most changes",
2737
+ title: "\u{1F7E1} Conservative",
2317
2738
  value: "conservative",
2739
+ description: "Ask before most changes",
2318
2740
  always: ["Read any file", "Run lint/format commands"],
2319
2741
  askFirst: ["Modify any file", "Add dependencies", "Create files", "Run tests"],
2320
2742
  never: ["Delete files", "Modify .env", "Push to git"]
2321
2743
  },
2322
2744
  {
2323
- title: "Permissive - AI can modify freely within src/",
2745
+ title: "\u{1F7E0} Permissive",
2324
2746
  value: "permissive",
2747
+ description: "AI can modify freely",
2325
2748
  always: ["Modify any file in src/", "Run any script", "Add dependencies", "Create files"],
2326
2749
  askFirst: ["Modify root configs", "Delete directories"],
2327
2750
  never: ["Modify .env", "Access external APIs without confirmation"]
2328
2751
  }
2329
2752
  ];
2753
+ var BOUNDARY_OPTIONS = [
2754
+ "Delete files",
2755
+ "Create new files",
2756
+ "Rename/move files",
2757
+ "Rewrite large sections",
2758
+ "Refactor architecture",
2759
+ "Change dependencies",
2760
+ "Modify database schema",
2761
+ "Update API contracts",
2762
+ "Touch CI pipelines",
2763
+ "Modify Docker config",
2764
+ "Change environment vars",
2765
+ "Update docs automatically",
2766
+ "Edit README",
2767
+ "Handle secrets/credentials",
2768
+ "Modify auth logic",
2769
+ "Delete failing tests",
2770
+ "Skip tests temporarily"
2771
+ ];
2772
+ var TEST_FRAMEWORKS = [
2773
+ "jest",
2774
+ "vitest",
2775
+ "mocha",
2776
+ "ava",
2777
+ "tap",
2778
+ "pytest",
2779
+ "unittest",
2780
+ "nose2",
2781
+ "go test",
2782
+ "testify",
2783
+ "cargo test",
2784
+ "rstest",
2785
+ "junit",
2786
+ "testng",
2787
+ "spock",
2788
+ "rspec",
2789
+ "minitest",
2790
+ "phpunit",
2791
+ "pest",
2792
+ "playwright",
2793
+ "cypress",
2794
+ "puppeteer",
2795
+ "selenium"
2796
+ ];
2797
+ var TEST_LEVELS = [
2798
+ { id: "smoke", label: "Smoke", desc: "Quick sanity checks" },
2799
+ { id: "unit", label: "Unit", desc: "Individual functions/components" },
2800
+ { id: "integration", label: "Integration", desc: "Component interactions" },
2801
+ { id: "e2e", label: "E2E", desc: "Full user flows" }
2802
+ ];
2803
+ var PROJECT_TYPES = [
2804
+ { id: "work", label: "Work", icon: "\u{1F4BC}", description: "Professional/enterprise project" },
2805
+ { id: "leisure", label: "Leisure", icon: "\u{1F3AE}", description: "Personal/hobby project" },
2806
+ { id: "opensource", label: "Open Source", icon: "\u{1F30D}", description: "Community-driven project" },
2807
+ { id: "learning", label: "Learning", icon: "\u{1F4DA}", description: "Educational/experimental" }
2808
+ ];
2809
+ function canAccessTier(userTier, requiredTier) {
2810
+ const tierLevels = { free: 0, pro: 1, max: 2, teams: 2 };
2811
+ const requiredLevels = { basic: 0, intermediate: 1, advanced: 2 };
2812
+ return tierLevels[userTier] >= requiredLevels[requiredTier];
2813
+ }
2814
+ function getTierBadge(tier) {
2815
+ switch (tier) {
2816
+ case "intermediate":
2817
+ return { label: "PRO", color: chalk8.cyan };
2818
+ case "advanced":
2819
+ return { label: "MAX", color: chalk8.magenta };
2820
+ default:
2821
+ return null;
2822
+ }
2823
+ }
2824
+ function getAvailableSteps(userTier) {
2825
+ return WIZARD_STEPS.filter((step) => canAccessTier(userTier, step.tier));
2826
+ }
2827
+ function printBox(lines, color = chalk8.gray) {
2828
+ const maxLen = Math.max(...lines.map((l) => l.replace(/\x1b\[[0-9;]*m/g, "").length));
2829
+ const top = "\u250C" + "\u2500".repeat(maxLen + 2) + "\u2510";
2830
+ const bottom = "\u2514" + "\u2500".repeat(maxLen + 2) + "\u2518";
2831
+ console.log(color(top));
2832
+ for (const line of lines) {
2833
+ const stripped = line.replace(/\x1b\[[0-9;]*m/g, "");
2834
+ const padding = " ".repeat(maxLen - stripped.length);
2835
+ console.log(color("\u2502 ") + line + padding + color(" \u2502"));
2836
+ }
2837
+ console.log(color(bottom));
2838
+ }
2839
+ function showStep(current, step, userTier) {
2840
+ const availableSteps = getAvailableSteps(userTier);
2841
+ const total = availableSteps.length;
2842
+ const progress = "\u25CF".repeat(current) + "\u25CB".repeat(total - current);
2843
+ const badge = getTierBadge(step.tier);
2844
+ console.log();
2845
+ let stepLine = chalk8.cyan(` ${progress} Step ${current}/${total}: ${step.icon} ${step.title}`);
2846
+ if (badge) {
2847
+ stepLine += " " + badge.color(`[${badge.label}]`);
2848
+ }
2849
+ console.log(stepLine);
2850
+ console.log();
2851
+ }
2852
+ function showWizardOverview(userTier) {
2853
+ console.log(chalk8.bold(" \u{1F4CB} Wizard Steps Overview:"));
2854
+ console.log();
2855
+ let stepNum = 1;
2856
+ for (const step of WIZARD_STEPS) {
2857
+ const canAccess = canAccessTier(userTier, step.tier);
2858
+ const badge = getTierBadge(step.tier);
2859
+ if (canAccess) {
2860
+ let line = chalk8.green(` ${stepNum.toString().padStart(2)}. \u2713 ${step.icon} ${step.title}`);
2861
+ if (badge) {
2862
+ line += " " + badge.color(`[${badge.label}]`);
2863
+ }
2864
+ console.log(line);
2865
+ stepNum++;
2866
+ } else {
2867
+ let line = chalk8.gray(` \u2500 \u{1F512} ${step.icon} ${step.title}`);
2868
+ if (badge) {
2869
+ line += " " + badge.color.dim(`[${badge.label}]`);
2870
+ }
2871
+ console.log(line);
2872
+ }
2873
+ }
2874
+ console.log();
2875
+ }
2876
+ var promptConfig = {
2877
+ onCancel: () => {
2878
+ console.log(chalk8.yellow("\n Cancelled. Run 'lynxp wizard' anytime to restart.\n"));
2879
+ process.exit(0);
2880
+ }
2881
+ };
2330
2882
  async function wizardCommand(options) {
2331
2883
  console.log();
2332
- console.log(chalk8.cyan("\u{1F431} LynxPrompt Wizard"));
2333
- console.log(chalk8.gray("Generate AI IDE configuration in seconds"));
2884
+ console.log(chalk8.cyan.bold(" \u{1F431} LynxPrompt Wizard"));
2885
+ console.log(chalk8.gray(" Generate AI IDE configuration in seconds"));
2334
2886
  console.log();
2887
+ const authenticated = isAuthenticated();
2888
+ const user = getUser();
2889
+ const userPlanRaw = user?.plan?.toLowerCase() || "free";
2890
+ const userTier = ["pro", "max", "teams"].includes(userPlanRaw) ? userPlanRaw : "free";
2891
+ const userPlanDisplay = user?.plan?.toUpperCase() || "FREE";
2892
+ if (!authenticated) {
2893
+ console.log(chalk8.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2894
+ console.log(chalk8.yellow("\u2502") + chalk8.white(" \u{1F4A1} ") + chalk8.gray("Log in for full wizard features:") + " " + chalk8.yellow("\u2502"));
2895
+ console.log(chalk8.yellow("\u2502") + " " + chalk8.yellow("\u2502"));
2896
+ console.log(chalk8.yellow("\u2502") + chalk8.gray(" \u2022 ") + chalk8.white("Commands & Code Style") + chalk8.cyan(" [PRO]") + " " + chalk8.yellow("\u2502"));
2897
+ console.log(chalk8.yellow("\u2502") + chalk8.gray(" \u2022 ") + chalk8.white("Boundaries, Testing, Static Files") + chalk8.magenta(" [MAX]") + " " + chalk8.yellow("\u2502"));
2898
+ console.log(chalk8.yellow("\u2502") + chalk8.gray(" \u2022 ") + chalk8.white("Push configs to cloud") + chalk8.gray(" (lynxp push)") + " " + chalk8.yellow("\u2502"));
2899
+ console.log(chalk8.yellow("\u2502") + chalk8.gray(" \u2022 ") + chalk8.white("Sync across devices") + chalk8.gray(" (lynxp sync)") + " " + chalk8.yellow("\u2502"));
2900
+ console.log(chalk8.yellow("\u2502") + " " + chalk8.yellow("\u2502"));
2901
+ console.log(chalk8.yellow("\u2502") + chalk8.cyan(" Run: lynxp login") + " " + chalk8.yellow("\u2502"));
2902
+ console.log(chalk8.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2903
+ console.log();
2904
+ } else {
2905
+ const planEmoji = userTier === "teams" ? "\u{1F465}" : userTier === "max" ? "\u{1F680}" : userTier === "pro" ? "\u26A1" : "\u{1F193}";
2906
+ console.log(chalk8.green(` \u2713 Logged in as ${chalk8.bold(user?.name || user?.email)} ${planEmoji} ${chalk8.gray(userPlanDisplay)}`));
2907
+ console.log();
2908
+ }
2909
+ showWizardOverview(userTier);
2910
+ const accessibleSteps = getAvailableSteps(userTier);
2911
+ const lockedSteps = WIZARD_STEPS.length - accessibleSteps.length;
2912
+ if (lockedSteps > 0) {
2913
+ console.log(chalk8.gray(` ${lockedSteps} step${lockedSteps > 1 ? "s" : ""} locked. Upgrade at ${chalk8.cyan("https://lynxprompt.com/pricing")}`));
2914
+ console.log();
2915
+ }
2335
2916
  const detected = await detectProject(process.cwd());
2336
2917
  if (detected) {
2337
- console.log(chalk8.green("\u2713 Detected project:"));
2338
- if (detected.name) console.log(chalk8.gray(` Name: ${detected.name}`));
2339
- if (detected.stack.length > 0) console.log(chalk8.gray(` Stack: ${detected.stack.join(", ")}`));
2340
- if (detected.packageManager) console.log(chalk8.gray(` Package manager: ${detected.packageManager}`));
2341
- if (detected.commands.build) console.log(chalk8.gray(` Build: ${detected.commands.build}`));
2342
- if (detected.commands.test) console.log(chalk8.gray(` Test: ${detected.commands.test}`));
2918
+ const detectedInfo = [
2919
+ chalk8.green("\u2713 Project detected")
2920
+ ];
2921
+ if (detected.name) detectedInfo.push(chalk8.gray(` Name: ${detected.name}`));
2922
+ if (detected.stack.length > 0) detectedInfo.push(chalk8.gray(` Stack: ${detected.stack.join(", ")}`));
2923
+ if (detected.packageManager) detectedInfo.push(chalk8.gray(` Package manager: ${detected.packageManager}`));
2924
+ printBox(detectedInfo, chalk8.gray);
2343
2925
  console.log();
2344
2926
  }
2345
2927
  let config2;
@@ -2362,19 +2944,20 @@ async function wizardCommand(options) {
2362
2944
  commands: detected?.commands || {}
2363
2945
  };
2364
2946
  } else {
2365
- config2 = await runInteractiveWizard(options, detected);
2947
+ config2 = await runInteractiveWizard(options, detected, userTier);
2366
2948
  }
2367
2949
  const spinner = ora7("Generating configuration...").start();
2368
2950
  try {
2369
2951
  const files = generateConfig(config2);
2370
2952
  spinner.stop();
2371
2953
  console.log();
2372
- console.log(chalk8.green("\u2705 Generated:"));
2954
+ console.log(chalk8.green.bold(" \u2705 Generated:"));
2955
+ console.log();
2373
2956
  for (const [filename, content] of Object.entries(files)) {
2374
2957
  const outputPath = join6(process.cwd(), filename);
2375
2958
  let exists = false;
2376
2959
  try {
2377
- await access5(outputPath);
2960
+ await access3(outputPath);
2378
2961
  exists = true;
2379
2962
  } catch {
2380
2963
  }
@@ -2386,7 +2969,7 @@ async function wizardCommand(options) {
2386
2969
  initial: false
2387
2970
  });
2388
2971
  if (!response.overwrite) {
2389
- console.log(chalk8.yellow(` Skipped: ${filename}`));
2972
+ console.log(chalk8.yellow(` \u23ED\uFE0F Skipped: ${filename}`));
2390
2973
  continue;
2391
2974
  }
2392
2975
  }
@@ -2395,15 +2978,24 @@ async function wizardCommand(options) {
2395
2978
  await mkdir4(dir, { recursive: true });
2396
2979
  }
2397
2980
  await writeFile4(outputPath, content, "utf-8");
2398
- console.log(` ${chalk8.cyan(filename)}`);
2981
+ console.log(` ${chalk8.cyan("\u2192")} ${chalk8.bold(filename)}`);
2399
2982
  }
2400
2983
  console.log();
2401
- console.log(chalk8.gray("Your AI assistant will now follow these instructions."));
2402
- console.log();
2403
- console.log(chalk8.gray("Tips:"));
2404
- console.log(chalk8.gray(" \u2022 Edit the generated file anytime to customize rules"));
2405
- console.log(chalk8.gray(" \u2022 Run 'lynxp wizard' again to regenerate"));
2406
- console.log(chalk8.gray(" \u2022 Run 'lynxp check' to validate your configuration"));
2984
+ const nextStepsLines = [
2985
+ chalk8.gray("Your AI assistant will now follow these instructions."),
2986
+ "",
2987
+ chalk8.gray("Next steps:"),
2988
+ chalk8.cyan(" lynxp check ") + chalk8.gray("Validate configuration")
2989
+ ];
2990
+ if (authenticated) {
2991
+ nextStepsLines.push(chalk8.cyan(" lynxp push ") + chalk8.gray("Upload to cloud"));
2992
+ nextStepsLines.push(chalk8.cyan(" lynxp link ") + chalk8.gray("Link to a blueprint"));
2993
+ nextStepsLines.push(chalk8.cyan(" lynxp sync ") + chalk8.gray("Sync with linked blueprint"));
2994
+ } else {
2995
+ nextStepsLines.push(chalk8.gray(" lynxp login ") + chalk8.yellow("Log in to push & sync"));
2996
+ }
2997
+ nextStepsLines.push(chalk8.cyan(" lynxp status ") + chalk8.gray("View current setup"));
2998
+ printBox(nextStepsLines, chalk8.gray);
2407
2999
  console.log();
2408
3000
  } catch (error) {
2409
3001
  spinner.fail("Failed to generate files");
@@ -2415,121 +3007,476 @@ async function wizardCommand(options) {
2415
3007
  process.exit(1);
2416
3008
  }
2417
3009
  }
2418
- async function runInteractiveWizard(options, detected) {
3010
+ async function runInteractiveWizard(options, detected, userTier) {
2419
3011
  const answers = {};
3012
+ const availableSteps = getAvailableSteps(userTier);
3013
+ let currentStepNum = 0;
3014
+ const getCurrentStep = (stepId) => {
3015
+ const step = availableSteps.find((s) => s.id === stepId);
3016
+ if (step) {
3017
+ currentStepNum++;
3018
+ return step;
3019
+ }
3020
+ return null;
3021
+ };
3022
+ const formatStep = getCurrentStep("format");
3023
+ showStep(currentStepNum, formatStep, userTier);
2420
3024
  let platforms;
2421
3025
  if (options.format) {
2422
3026
  platforms = options.format.split(",").map((f) => f.trim());
3027
+ console.log(chalk8.gray(` Using format from flag: ${platforms.join(", ")}`));
2423
3028
  } else {
2424
3029
  const formatResponse = await prompts4({
2425
3030
  type: "select",
2426
3031
  name: "format",
2427
- message: "Select output format:",
3032
+ message: chalk8.white("Where will you use this?"),
2428
3033
  choices: OUTPUT_FORMATS.map((f) => ({
2429
- title: f.recommended ? `${f.title} ${chalk8.green("(recommended)")}` : f.title,
3034
+ title: f.recommended ? `${f.title} ${chalk8.green.bold("\u2605 recommended")}` : f.title,
2430
3035
  value: f.value,
2431
- description: f.description
3036
+ description: chalk8.gray(f.description)
2432
3037
  })),
2433
- initial: 0
2434
- // AGENTS.md is default
2435
- });
3038
+ initial: 0,
3039
+ hint: chalk8.gray("\u2191\u2193 navigate \u2022 enter select")
3040
+ }, promptConfig);
2436
3041
  if (formatResponse.format === "multiple") {
3042
+ console.log();
3043
+ console.log(chalk8.gray(" Select the AI editors you want to generate config for:"));
3044
+ console.log();
2437
3045
  const platformResponse = await prompts4({
2438
3046
  type: "multiselect",
2439
3047
  name: "platforms",
2440
- message: "Select AI editors:",
2441
- choices: PLATFORMS.map((p) => ({ title: p.title, value: p.value })),
2442
- hint: "- Space to select, Enter to confirm",
2443
- min: 1
2444
- });
3048
+ message: chalk8.white("Select AI editors (16 supported):"),
3049
+ choices: ALL_PLATFORMS.map((p) => ({
3050
+ title: `${p.icon} ${p.name}`,
3051
+ value: p.id,
3052
+ description: chalk8.gray(p.note)
3053
+ })),
3054
+ hint: chalk8.gray("space select \u2022 a toggle all \u2022 enter confirm"),
3055
+ min: 1,
3056
+ instructions: false
3057
+ }, promptConfig);
2445
3058
  platforms = platformResponse.platforms || ["agents"];
3059
+ console.log(chalk8.green(` \u2713 Selected ${platforms.length} platform${platforms.length === 1 ? "" : "s"}`));
2446
3060
  } else {
2447
3061
  platforms = [formatResponse.format || "agents"];
2448
3062
  }
2449
3063
  }
2450
3064
  answers.platforms = platforms;
3065
+ const projectStep = getCurrentStep("project");
3066
+ showStep(currentStepNum, projectStep, userTier);
2451
3067
  const nameResponse = await prompts4({
2452
3068
  type: "text",
2453
3069
  name: "name",
2454
- message: "Project name:",
2455
- initial: options.name || detected?.name || "my-project"
2456
- });
3070
+ message: chalk8.white("Project name:"),
3071
+ initial: options.name || detected?.name || "my-project",
3072
+ hint: chalk8.gray("Used in the generated config header")
3073
+ }, promptConfig);
2457
3074
  answers.name = nameResponse.name || "my-project";
2458
3075
  const descResponse = await prompts4({
2459
3076
  type: "text",
2460
3077
  name: "description",
2461
- message: "Brief description (optional):",
2462
- initial: options.description || ""
2463
- });
3078
+ message: chalk8.white("Brief description:"),
3079
+ initial: options.description || "",
3080
+ hint: chalk8.gray("optional - helps AI understand context")
3081
+ }, promptConfig);
2464
3082
  answers.description = descResponse.description || "";
2465
- const allStackOptions = [...TECH_STACKS, ...FRAMEWORKS];
3083
+ const typeResponse = await prompts4({
3084
+ type: "select",
3085
+ name: "projectType",
3086
+ message: chalk8.white("Project type:"),
3087
+ choices: PROJECT_TYPES.map((t) => ({
3088
+ title: `${t.icon} ${t.label}`,
3089
+ value: t.id,
3090
+ description: chalk8.gray(t.description)
3091
+ })),
3092
+ initial: 0
3093
+ }, promptConfig);
3094
+ answers.projectType = typeResponse.projectType || "work";
3095
+ const techStep = getCurrentStep("tech");
3096
+ showStep(currentStepNum, techStep, userTier);
3097
+ const allStackOptions = [...LANGUAGES, ...FRAMEWORKS, ...DATABASES];
2466
3098
  const detectedStackSet = new Set(detected?.stack || []);
3099
+ if (detectedStackSet.size > 0) {
3100
+ console.log(chalk8.gray(` Auto-selected: ${detected?.stack?.join(", ")}`));
3101
+ console.log();
3102
+ }
2467
3103
  const stackResponse = await prompts4({
2468
3104
  type: "multiselect",
2469
3105
  name: "stack",
2470
- message: "Tech stack:",
3106
+ message: chalk8.white("Languages, frameworks & databases:"),
2471
3107
  choices: allStackOptions.map((s) => ({
2472
3108
  title: s.title,
2473
3109
  value: s.value,
2474
3110
  selected: detectedStackSet.has(s.value)
2475
3111
  })),
2476
- hint: "- Space to select, Enter to confirm"
2477
- });
3112
+ hint: chalk8.gray("space select \u2022 a toggle all \u2022 enter confirm"),
3113
+ instructions: false
3114
+ }, promptConfig);
2478
3115
  answers.stack = stackResponse.stack || [];
3116
+ const repoStep = getCurrentStep("repo");
3117
+ showStep(currentStepNum, repoStep, userTier);
3118
+ const repoHostResponse = await prompts4({
3119
+ type: "select",
3120
+ name: "repoHost",
3121
+ message: chalk8.white("Repository host:"),
3122
+ choices: REPO_HOSTS.map((h) => ({
3123
+ title: `${h.icon} ${h.label}`,
3124
+ value: h.id
3125
+ })),
3126
+ initial: 0
3127
+ }, promptConfig);
3128
+ answers.repoHost = repoHostResponse.repoHost || "github";
3129
+ const visibilityResponse = await prompts4({
3130
+ type: "toggle",
3131
+ name: "isPublic",
3132
+ message: chalk8.white("Public repository?"),
3133
+ initial: false,
3134
+ active: "Yes",
3135
+ inactive: "No"
3136
+ }, promptConfig);
3137
+ answers.isPublic = visibilityResponse.isPublic || false;
3138
+ const licenseResponse = await prompts4({
3139
+ type: "select",
3140
+ name: "license",
3141
+ message: chalk8.white("License:"),
3142
+ choices: LICENSES.map((l) => ({
3143
+ title: l.label,
3144
+ value: l.id
3145
+ })),
3146
+ initial: 0
3147
+ }, promptConfig);
3148
+ answers.license = licenseResponse.license || "mit";
3149
+ const conventionalResponse = await prompts4({
3150
+ type: "toggle",
3151
+ name: "conventionalCommits",
3152
+ message: chalk8.white("Use Conventional Commits?"),
3153
+ initial: true,
3154
+ active: "Yes",
3155
+ inactive: "No"
3156
+ }, promptConfig);
3157
+ answers.conventionalCommits = conventionalResponse.conventionalCommits ?? true;
3158
+ if (canAccessTier(userTier, "intermediate")) {
3159
+ const commandsStep = getCurrentStep("commands");
3160
+ showStep(currentStepNum, commandsStep, userTier);
3161
+ console.log(chalk8.gray(" Select common commands for your project:"));
3162
+ console.log();
3163
+ const buildResponse = await prompts4({
3164
+ type: "multiselect",
3165
+ name: "build",
3166
+ message: chalk8.white("Build commands:"),
3167
+ choices: COMMON_COMMANDS.build.slice(0, 12).map((c) => ({
3168
+ title: chalk8.cyan(c),
3169
+ value: c,
3170
+ selected: detected?.commands?.build === c
3171
+ })),
3172
+ hint: chalk8.gray("space select \u2022 enter confirm"),
3173
+ instructions: false
3174
+ }, promptConfig);
3175
+ const testResponse = await prompts4({
3176
+ type: "multiselect",
3177
+ name: "test",
3178
+ message: chalk8.white("Test commands:"),
3179
+ choices: COMMON_COMMANDS.test.slice(0, 12).map((c) => ({
3180
+ title: chalk8.yellow(c),
3181
+ value: c,
3182
+ selected: detected?.commands?.test === c
3183
+ })),
3184
+ hint: chalk8.gray("space select \u2022 enter confirm"),
3185
+ instructions: false
3186
+ }, promptConfig);
3187
+ const lintResponse = await prompts4({
3188
+ type: "multiselect",
3189
+ name: "lint",
3190
+ message: chalk8.white("Lint/format commands:"),
3191
+ choices: COMMON_COMMANDS.lint.slice(0, 12).map((c) => ({
3192
+ title: chalk8.green(c),
3193
+ value: c,
3194
+ selected: detected?.commands?.lint === c
3195
+ })),
3196
+ hint: chalk8.gray("space select \u2022 enter confirm"),
3197
+ instructions: false
3198
+ }, promptConfig);
3199
+ const devResponse = await prompts4({
3200
+ type: "multiselect",
3201
+ name: "dev",
3202
+ message: chalk8.white("Dev server commands:"),
3203
+ choices: COMMON_COMMANDS.dev.slice(0, 12).map((c) => ({
3204
+ title: chalk8.magenta(c),
3205
+ value: c,
3206
+ selected: detected?.commands?.dev === c
3207
+ })),
3208
+ hint: chalk8.gray("space select \u2022 enter confirm"),
3209
+ instructions: false
3210
+ }, promptConfig);
3211
+ answers.commands = {
3212
+ build: buildResponse.build || [],
3213
+ test: testResponse.test || [],
3214
+ lint: lintResponse.lint || [],
3215
+ dev: devResponse.dev || []
3216
+ };
3217
+ const customCmdResponse = await prompts4({
3218
+ type: "text",
3219
+ name: "custom",
3220
+ message: chalk8.white("Additional custom command (optional):"),
3221
+ hint: chalk8.gray("e.g., npm run migrate, make deploy")
3222
+ }, promptConfig);
3223
+ if (customCmdResponse.custom) {
3224
+ answers.commands.custom = customCmdResponse.custom;
3225
+ }
3226
+ } else {
3227
+ answers.commands = detected?.commands || {};
3228
+ }
3229
+ if (canAccessTier(userTier, "intermediate")) {
3230
+ const styleStep = getCurrentStep("code_style");
3231
+ showStep(currentStepNum, styleStep, userTier);
3232
+ const namingResponse = await prompts4({
3233
+ type: "select",
3234
+ name: "naming",
3235
+ message: chalk8.white("Naming convention:"),
3236
+ choices: NAMING_CONVENTIONS.map((n) => ({
3237
+ title: n.label,
3238
+ value: n.id,
3239
+ description: chalk8.gray(n.desc)
3240
+ })),
3241
+ initial: 0
3242
+ }, promptConfig);
3243
+ answers.namingConvention = namingResponse.naming || "language_default";
3244
+ const errorResponse = await prompts4({
3245
+ type: "select",
3246
+ name: "errorHandling",
3247
+ message: chalk8.white("Error handling pattern:"),
3248
+ choices: ERROR_PATTERNS.map((e) => ({
3249
+ title: e.label,
3250
+ value: e.id
3251
+ })),
3252
+ initial: 0
3253
+ }, promptConfig);
3254
+ answers.errorHandling = errorResponse.errorHandling || "try_catch";
3255
+ const styleNotesResponse = await prompts4({
3256
+ type: "text",
3257
+ name: "styleNotes",
3258
+ message: chalk8.white("Additional style notes (optional):"),
3259
+ hint: chalk8.gray("e.g., prefer named exports, max line length 100")
3260
+ }, promptConfig);
3261
+ answers.styleNotes = styleNotesResponse.styleNotes || "";
3262
+ }
3263
+ const aiStep = getCurrentStep("ai");
3264
+ showStep(currentStepNum, aiStep, userTier);
3265
+ const aiBehaviorResponse = await prompts4({
3266
+ type: "multiselect",
3267
+ name: "aiBehavior",
3268
+ message: chalk8.white("AI behavior rules:"),
3269
+ choices: AI_BEHAVIOR_RULES.map((r) => ({
3270
+ title: r.recommended ? `${r.label} ${chalk8.green("\u2605")}` : r.label,
3271
+ value: r.id,
3272
+ selected: r.recommended
3273
+ })),
3274
+ hint: chalk8.gray("space select \u2022 enter confirm"),
3275
+ instructions: false
3276
+ }, promptConfig);
3277
+ answers.aiBehavior = aiBehaviorResponse.aiBehavior || [];
3278
+ const importantFilesResponse = await prompts4({
3279
+ type: "multiselect",
3280
+ name: "importantFiles",
3281
+ message: chalk8.white("Important files AI should read:"),
3282
+ choices: IMPORTANT_FILES.map((f) => ({
3283
+ title: `${f.icon} ${f.label}`,
3284
+ value: f.id,
3285
+ selected: f.id === "readme" || f.id === "package"
3286
+ })),
3287
+ hint: chalk8.gray("space select \u2022 enter confirm"),
3288
+ instructions: false
3289
+ }, promptConfig);
3290
+ answers.importantFiles = importantFilesResponse.importantFiles || [];
3291
+ const selfImproveResponse = await prompts4({
3292
+ type: "toggle",
3293
+ name: "selfImprove",
3294
+ message: chalk8.white("Enable self-improving blueprint?"),
3295
+ initial: false,
3296
+ active: "Yes",
3297
+ inactive: "No"
3298
+ }, promptConfig);
3299
+ answers.selfImprove = selfImproveResponse.selfImprove || false;
3300
+ if (canAccessTier(userTier, "advanced")) {
3301
+ const boundariesStep = getCurrentStep("boundaries");
3302
+ showStep(currentStepNum, boundariesStep, userTier);
3303
+ const presetResponse = await prompts4({
3304
+ type: "select",
3305
+ name: "boundaryPreset",
3306
+ message: chalk8.white("Boundary preset:"),
3307
+ choices: BOUNDARY_PRESETS.map((b) => ({
3308
+ title: b.title,
3309
+ value: b.value,
3310
+ description: chalk8.gray(b.description)
3311
+ })),
3312
+ initial: 0
3313
+ }, promptConfig);
3314
+ answers.boundaries = presetResponse.boundaryPreset || "standard";
3315
+ const selectedPreset = BOUNDARY_PRESETS.find((b) => b.value === answers.boundaries);
3316
+ if (selectedPreset) {
3317
+ console.log();
3318
+ console.log(chalk8.gray(" Preset details:"));
3319
+ console.log(chalk8.green(` \u2713 Always: ${selectedPreset.always.slice(0, 3).join(", ")}`));
3320
+ console.log(chalk8.yellow(` ? Ask: ${selectedPreset.askFirst.slice(0, 2).join(", ")}`));
3321
+ console.log(chalk8.red(` \u2717 Never: ${selectedPreset.never.slice(0, 2).join(", ")}`));
3322
+ }
3323
+ const customizeResponse = await prompts4({
3324
+ type: "toggle",
3325
+ name: "customize",
3326
+ message: chalk8.white("Customize specific boundaries?"),
3327
+ initial: false,
3328
+ active: "Yes",
3329
+ inactive: "No"
3330
+ }, promptConfig);
3331
+ if (customizeResponse.customize) {
3332
+ console.log();
3333
+ console.log(chalk8.gray(" Select actions AI should NEVER do:"));
3334
+ const neverResponse = await prompts4({
3335
+ type: "multiselect",
3336
+ name: "never",
3337
+ message: chalk8.white("Never allow:"),
3338
+ choices: BOUNDARY_OPTIONS.map((o) => ({
3339
+ title: chalk8.red(o),
3340
+ value: o,
3341
+ selected: selectedPreset?.never.includes(o)
3342
+ })),
3343
+ instructions: false
3344
+ }, promptConfig);
3345
+ answers.boundaryNever = neverResponse.never || [];
3346
+ console.log(chalk8.gray(" Select actions AI should ASK before doing:"));
3347
+ const askResponse = await prompts4({
3348
+ type: "multiselect",
3349
+ name: "ask",
3350
+ message: chalk8.white("Ask first:"),
3351
+ choices: BOUNDARY_OPTIONS.filter((o) => !answers.boundaryNever?.includes(o)).map((o) => ({
3352
+ title: chalk8.yellow(o),
3353
+ value: o,
3354
+ selected: selectedPreset?.askFirst.includes(o)
3355
+ })),
3356
+ instructions: false
3357
+ }, promptConfig);
3358
+ answers.boundaryAsk = askResponse.ask || [];
3359
+ }
3360
+ } else {
3361
+ answers.boundaries = options.boundaries || "standard";
3362
+ }
3363
+ if (canAccessTier(userTier, "advanced")) {
3364
+ const testingStep = getCurrentStep("testing");
3365
+ showStep(currentStepNum, testingStep, userTier);
3366
+ const testLevelsResponse = await prompts4({
3367
+ type: "multiselect",
3368
+ name: "testLevels",
3369
+ message: chalk8.white("Test levels:"),
3370
+ choices: TEST_LEVELS.map((l) => ({
3371
+ title: `${l.label} - ${chalk8.gray(l.desc)}`,
3372
+ value: l.id,
3373
+ selected: l.id === "unit" || l.id === "integration"
3374
+ })),
3375
+ instructions: false
3376
+ }, promptConfig);
3377
+ answers.testLevels = testLevelsResponse.testLevels || [];
3378
+ const detectedFrameworks = answers.stack?.includes("typescript") || answers.stack?.includes("javascript") ? ["jest", "vitest"] : answers.stack?.includes("python") ? ["pytest"] : [];
3379
+ const testFrameworkResponse = await prompts4({
3380
+ type: "multiselect",
3381
+ name: "testFrameworks",
3382
+ message: chalk8.white("Testing frameworks:"),
3383
+ choices: TEST_FRAMEWORKS.slice(0, 16).map((f) => ({
3384
+ title: f,
3385
+ value: f,
3386
+ selected: detectedFrameworks.includes(f)
3387
+ })),
3388
+ hint: chalk8.gray("space select \u2022 enter confirm"),
3389
+ instructions: false
3390
+ }, promptConfig);
3391
+ answers.testFrameworks = testFrameworkResponse.testFrameworks || [];
3392
+ const coverageResponse = await prompts4({
3393
+ type: "number",
3394
+ name: "coverage",
3395
+ message: chalk8.white("Target code coverage (%):"),
3396
+ initial: 80,
3397
+ min: 0,
3398
+ max: 100
3399
+ }, promptConfig);
3400
+ answers.coverageTarget = coverageResponse.coverage ?? 80;
3401
+ const testNotesResponse = await prompts4({
3402
+ type: "text",
3403
+ name: "testNotes",
3404
+ message: chalk8.white("Testing notes (optional):"),
3405
+ hint: chalk8.gray("e.g., run e2e on main only, use msw for mocking")
3406
+ }, promptConfig);
3407
+ answers.testNotes = testNotesResponse.testNotes || "";
3408
+ }
3409
+ if (canAccessTier(userTier, "advanced")) {
3410
+ const staticStep = getCurrentStep("static");
3411
+ showStep(currentStepNum, staticStep, userTier);
3412
+ console.log(chalk8.gray(" Generate additional project files:"));
3413
+ console.log();
3414
+ const staticFilesResponse = await prompts4({
3415
+ type: "multiselect",
3416
+ name: "staticFiles",
3417
+ message: chalk8.white("Include static files:"),
3418
+ choices: [
3419
+ { title: "\u{1F4DD} .editorconfig", value: "editorconfig", description: chalk8.gray("Consistent code formatting") },
3420
+ { title: "\u{1F91D} CONTRIBUTING.md", value: "contributing", description: chalk8.gray("Contributor guidelines") },
3421
+ { title: "\u{1F4DC} CODE_OF_CONDUCT.md", value: "codeOfConduct", description: chalk8.gray("Community standards") },
3422
+ { title: "\u{1F512} SECURITY.md", value: "security", description: chalk8.gray("Vulnerability reporting") },
3423
+ { title: "\u{1F5FA}\uFE0F ROADMAP.md", value: "roadmap", description: chalk8.gray("Project roadmap") },
3424
+ { title: "\u{1F4CB} .gitignore", value: "gitignore", description: chalk8.gray("Git ignore patterns"), selected: true }
3425
+ ],
3426
+ hint: chalk8.gray("space select \u2022 enter confirm"),
3427
+ instructions: false
3428
+ }, promptConfig);
3429
+ answers.staticFiles = staticFilesResponse.staticFiles || [];
3430
+ if (answers.repoHost === "github" && answers.isPublic) {
3431
+ const fundingResponse = await prompts4({
3432
+ type: "toggle",
3433
+ name: "funding",
3434
+ message: chalk8.white("Generate FUNDING.yml for GitHub Sponsors?"),
3435
+ initial: false,
3436
+ active: "Yes",
3437
+ inactive: "No"
3438
+ }, promptConfig);
3439
+ answers.includeFunding = fundingResponse.funding || false;
3440
+ }
3441
+ }
3442
+ const extraStep = getCurrentStep("extra");
3443
+ showStep(currentStepNum, extraStep, userTier);
2479
3444
  const personaResponse = await prompts4({
2480
3445
  type: "select",
2481
3446
  name: "persona",
2482
- message: "AI persona:",
2483
- choices: PERSONAS,
3447
+ message: chalk8.white("AI assistant persona:"),
3448
+ choices: [
3449
+ { title: "\u{1F9D1}\u200D\u{1F4BB} Full-Stack Developer", value: "fullstack", description: chalk8.gray("Complete application development") },
3450
+ { title: "\u2699\uFE0F Backend Developer", value: "backend", description: chalk8.gray("APIs, databases, services") },
3451
+ { title: "\u{1F3A8} Frontend Developer", value: "frontend", description: chalk8.gray("UI, components, styling") },
3452
+ { title: "\u{1F680} DevOps Engineer", value: "devops", description: chalk8.gray("Infrastructure, CI/CD") },
3453
+ { title: "\u{1F4CA} Data Engineer", value: "data", description: chalk8.gray("Pipelines, ETL, analytics") },
3454
+ { title: "\u{1F512} Security Engineer", value: "security", description: chalk8.gray("Secure code, auditing") },
3455
+ { title: "\u270F\uFE0F Custom...", value: "custom", description: chalk8.gray("Define your own") }
3456
+ ],
2484
3457
  initial: 0
2485
- // Full-stack by default
2486
- });
3458
+ }, promptConfig);
2487
3459
  if (personaResponse.persona === "custom") {
2488
3460
  const customPersona = await prompts4({
2489
3461
  type: "text",
2490
3462
  name: "value",
2491
- message: "Describe the custom persona:"
2492
- });
3463
+ message: chalk8.white("Describe the custom persona:"),
3464
+ hint: chalk8.gray("e.g., 'ML engineer focused on PyTorch'")
3465
+ }, promptConfig);
2493
3466
  answers.persona = customPersona.value || "fullstack";
2494
3467
  } else {
2495
3468
  answers.persona = personaResponse.persona || "fullstack";
2496
3469
  }
2497
- const boundaryResponse = await prompts4({
2498
- type: "select",
2499
- name: "boundaries",
2500
- message: "AI boundaries:",
2501
- choices: BOUNDARY_PRESETS.map((b) => ({ title: b.title, value: b.value })),
2502
- initial: 0
2503
- // Standard by default
2504
- });
2505
- answers.boundaries = boundaryResponse.boundaries || "standard";
2506
- if (detected?.commands && Object.keys(detected.commands).length > 0) {
2507
- console.log();
2508
- console.log(chalk8.gray("Auto-detected commands:"));
2509
- if (detected.commands.build) console.log(chalk8.gray(` Build: ${detected.commands.build}`));
2510
- if (detected.commands.test) console.log(chalk8.gray(` Test: ${detected.commands.test}`));
2511
- if (detected.commands.lint) console.log(chalk8.gray(` Lint: ${detected.commands.lint}`));
2512
- if (detected.commands.dev) console.log(chalk8.gray(` Dev: ${detected.commands.dev}`));
2513
- const editCommands = await prompts4({
2514
- type: "confirm",
2515
- name: "edit",
2516
- message: "Edit commands?",
2517
- initial: false
2518
- });
2519
- if (editCommands.edit) {
2520
- const commandsResponse = await prompts4([
2521
- { type: "text", name: "build", message: "Build:", initial: detected.commands.build },
2522
- { type: "text", name: "test", message: "Test:", initial: detected.commands.test },
2523
- { type: "text", name: "lint", message: "Lint:", initial: detected.commands.lint },
2524
- { type: "text", name: "dev", message: "Dev:", initial: detected.commands.dev }
2525
- ]);
2526
- answers.commands = commandsResponse;
2527
- } else {
2528
- answers.commands = detected.commands;
2529
- }
2530
- } else {
2531
- answers.commands = {};
2532
- }
3470
+ const extraNotesResponse = await prompts4({
3471
+ type: "text",
3472
+ name: "extraNotes",
3473
+ message: chalk8.white("Anything else AI should know? (optional):"),
3474
+ hint: chalk8.gray("Special requirements, gotchas, team conventions...")
3475
+ }, promptConfig);
3476
+ answers.extraNotes = extraNotesResponse.extraNotes || "";
3477
+ console.log();
3478
+ console.log(chalk8.green(" \u2705 All steps completed!"));
3479
+ console.log();
2533
3480
  return {
2534
3481
  name: answers.name,
2535
3482
  description: answers.description,
@@ -2537,7 +3484,28 @@ async function runInteractiveWizard(options, detected) {
2537
3484
  platforms: answers.platforms,
2538
3485
  persona: answers.persona,
2539
3486
  boundaries: answers.boundaries,
2540
- commands: answers.commands
3487
+ commands: typeof answers.commands === "object" ? answers.commands : detected?.commands || {},
3488
+ // Extended config for Pro/Max users
3489
+ projectType: answers.projectType,
3490
+ repoHost: answers.repoHost,
3491
+ isPublic: answers.isPublic,
3492
+ license: answers.license,
3493
+ conventionalCommits: answers.conventionalCommits,
3494
+ namingConvention: answers.namingConvention,
3495
+ errorHandling: answers.errorHandling,
3496
+ styleNotes: answers.styleNotes,
3497
+ aiBehavior: answers.aiBehavior,
3498
+ importantFiles: answers.importantFiles,
3499
+ selfImprove: answers.selfImprove,
3500
+ boundaryNever: answers.boundaryNever,
3501
+ boundaryAsk: answers.boundaryAsk,
3502
+ testLevels: answers.testLevels,
3503
+ testFrameworks: answers.testFrameworks,
3504
+ coverageTarget: answers.coverageTarget,
3505
+ testNotes: answers.testNotes,
3506
+ staticFiles: answers.staticFiles,
3507
+ includeFunding: answers.includeFunding,
3508
+ extraNotes: answers.extraNotes
2541
3509
  };
2542
3510
  }
2543
3511
 
@@ -2604,7 +3572,7 @@ function handleApiError3(error) {
2604
3572
 
2605
3573
  // src/commands/status.ts
2606
3574
  import chalk10 from "chalk";
2607
- import { access as access6, readFile as readFile6, readdir as readdir3 } from "fs/promises";
3575
+ import { readFile as readFile5, readdir, access as access4 } from "fs/promises";
2608
3576
  import { join as join7 } from "path";
2609
3577
  import { existsSync as existsSync4 } from "fs";
2610
3578
  var CONFIG_FILES = [
@@ -2634,7 +3602,7 @@ async function statusCommand() {
2634
3602
  const configPath = join7(cwd, ".lynxprompt/conf.yml");
2635
3603
  if (existsSync4(configPath)) {
2636
3604
  try {
2637
- const content = await readFile6(configPath, "utf-8");
3605
+ const content = await readFile5(configPath, "utf-8");
2638
3606
  const { parse: parse5 } = await import("yaml");
2639
3607
  const config2 = parse5(content);
2640
3608
  if (config2?.exporters?.length > 0) {
@@ -2677,8 +3645,8 @@ async function statusCommand() {
2677
3645
  for (const config2 of CONFIG_FILES) {
2678
3646
  const filePath = join7(cwd, config2.path);
2679
3647
  try {
2680
- await access6(filePath);
2681
- const content = await readFile6(filePath, "utf-8");
3648
+ await access4(filePath);
3649
+ const content = await readFile5(filePath, "utf-8");
2682
3650
  const lines = content.split("\n").length;
2683
3651
  const size = formatBytes(content.length);
2684
3652
  foundAny = true;
@@ -2699,7 +3667,7 @@ async function statusCommand() {
2699
3667
  const dirPath = join7(cwd, config2.path);
2700
3668
  if (existsSync4(dirPath)) {
2701
3669
  try {
2702
- const files = await readdir3(dirPath);
3670
+ const files = await readdir(dirPath);
2703
3671
  const ruleFiles = files.filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
2704
3672
  if (ruleFiles.length > 0) {
2705
3673
  foundAny = true;
@@ -2757,7 +3725,7 @@ function formatBytes(bytes) {
2757
3725
  import chalk11 from "chalk";
2758
3726
  import ora9 from "ora";
2759
3727
  import prompts5 from "prompts";
2760
- import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5, readdir as readdir4 } from "fs/promises";
3728
+ import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir5, readdir as readdir2 } from "fs/promises";
2761
3729
  import { join as join8, dirname as dirname5 } from "path";
2762
3730
  import { existsSync as existsSync5 } from "fs";
2763
3731
  import * as yaml3 from "yaml";
@@ -2778,7 +3746,7 @@ async function syncCommand(options = {}) {
2778
3746
  const spinner = ora9("Loading configuration...").start();
2779
3747
  let config2;
2780
3748
  try {
2781
- const configContent = await readFile7(configPath, "utf-8");
3749
+ const configContent = await readFile6(configPath, "utf-8");
2782
3750
  config2 = yaml3.parse(configContent);
2783
3751
  spinner.succeed("Configuration loaded");
2784
3752
  } catch (error) {
@@ -2875,12 +3843,12 @@ async function syncCommand(options = {}) {
2875
3843
  async function loadRules(rulesPath) {
2876
3844
  const files = [];
2877
3845
  try {
2878
- const entries = await readdir4(rulesPath, { withFileTypes: true });
3846
+ const entries = await readdir2(rulesPath, { withFileTypes: true });
2879
3847
  for (const entry of entries) {
2880
3848
  if (!entry.isFile()) continue;
2881
3849
  if (!entry.name.endsWith(".md")) continue;
2882
3850
  const filePath = join8(rulesPath, entry.name);
2883
- const content = await readFile7(filePath, "utf-8");
3851
+ const content = await readFile6(filePath, "utf-8");
2884
3852
  if (content.trim()) {
2885
3853
  files.push({ name: entry.name, content: content.trim() });
2886
3854
  }
@@ -2930,7 +3898,7 @@ function formatForAgent(agent, content) {
2930
3898
  return content;
2931
3899
  }
2932
3900
  }
2933
- function formatAsMdc(content, agent) {
3901
+ function formatAsMdc(content, _agent) {
2934
3902
  const frontmatter = yaml3.stringify({
2935
3903
  description: "LynxPrompt rules - AI coding guidelines",
2936
3904
  globs: ["**/*"],
@@ -2941,7 +3909,7 @@ ${frontmatter}---
2941
3909
 
2942
3910
  ${content}`;
2943
3911
  }
2944
- function formatAsMarkdown(content, agent) {
3912
+ function formatAsMarkdown(content, _agent) {
2945
3913
  const header = `# AI Coding Rules
2946
3914
 
2947
3915
  > Generated by [LynxPrompt](https://lynxprompt.com)
@@ -2949,7 +3917,7 @@ function formatAsMarkdown(content, agent) {
2949
3917
  `;
2950
3918
  return header + content;
2951
3919
  }
2952
- function formatAsJson(content, agent) {
3920
+ function formatAsJson(content, _agent) {
2953
3921
  return JSON.stringify(
2954
3922
  {
2955
3923
  $schema: "https://lynxprompt.com/schemas/rules.json",
@@ -2968,7 +3936,7 @@ function formatAsJson(content, agent) {
2968
3936
  // src/commands/agents.ts
2969
3937
  import chalk12 from "chalk";
2970
3938
  import prompts6 from "prompts";
2971
- import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
3939
+ import { readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
2972
3940
  import { join as join9 } from "path";
2973
3941
  import { existsSync as existsSync6 } from "fs";
2974
3942
  import * as yaml4 from "yaml";
@@ -3170,7 +4138,7 @@ async function loadConfig() {
3170
4138
  return null;
3171
4139
  }
3172
4140
  try {
3173
- const content = await readFile8(configPath, "utf-8");
4141
+ const content = await readFile7(configPath, "utf-8");
3174
4142
  return yaml4.parse(content);
3175
4143
  } catch {
3176
4144
  return null;
@@ -3186,7 +4154,7 @@ async function saveConfig(config2) {
3186
4154
  // src/commands/check.ts
3187
4155
  import chalk13 from "chalk";
3188
4156
  import ora10 from "ora";
3189
- import { readFile as readFile9, readdir as readdir5, stat as stat2 } from "fs/promises";
4157
+ import { readFile as readFile8, readdir as readdir3, stat } from "fs/promises";
3190
4158
  import { join as join10 } from "path";
3191
4159
  import { existsSync as existsSync7 } from "fs";
3192
4160
  import * as yaml5 from "yaml";
@@ -3252,7 +4220,7 @@ async function validateLynxPromptConfig(cwd) {
3252
4220
  return { errors, warnings };
3253
4221
  }
3254
4222
  try {
3255
- const content = await readFile9(configPath, "utf-8");
4223
+ const content = await readFile8(configPath, "utf-8");
3256
4224
  const config2 = yaml5.parse(content);
3257
4225
  if (!config2.version) {
3258
4226
  warnings.push(".lynxprompt/conf.yml: Missing 'version' field");
@@ -3325,7 +4293,7 @@ async function checkCommand(options = {}) {
3325
4293
  if (existsSync7(filePath)) {
3326
4294
  result.files.push(file.path);
3327
4295
  try {
3328
- const content = await readFile9(filePath, "utf-8");
4296
+ const content = await readFile8(filePath, "utf-8");
3329
4297
  const validation = validateMarkdown(content, file.path);
3330
4298
  result.errors.push(...validation.errors);
3331
4299
  result.warnings.push(...validation.warnings);
@@ -3338,13 +4306,13 @@ async function checkCommand(options = {}) {
3338
4306
  const dirPath = join10(cwd, dir.path);
3339
4307
  if (existsSync7(dirPath)) {
3340
4308
  try {
3341
- const files = await readdir5(dirPath);
4309
+ const files = await readdir3(dirPath);
3342
4310
  for (const file of files) {
3343
4311
  const filePath = join10(dirPath, file);
3344
- const fileStat = await stat2(filePath);
4312
+ const fileStat = await stat(filePath);
3345
4313
  if (fileStat.isFile()) {
3346
4314
  result.files.push(`${dir.path}/${file}`);
3347
- const content = await readFile9(filePath, "utf-8");
4315
+ const content = await readFile8(filePath, "utf-8");
3348
4316
  if (file.endsWith(".mdc")) {
3349
4317
  const validation = validateMdc(content, `${dir.path}/${file}`);
3350
4318
  result.errors.push(...validation.errors);
@@ -3420,7 +4388,7 @@ async function checkCommand(options = {}) {
3420
4388
  // src/commands/diff.ts
3421
4389
  import chalk14 from "chalk";
3422
4390
  import ora11 from "ora";
3423
- import { readFile as readFile10 } from "fs/promises";
4391
+ import { readFile as readFile9 } from "fs/promises";
3424
4392
  import { join as join11 } from "path";
3425
4393
  import { existsSync as existsSync8 } from "fs";
3426
4394
  function computeDiff(oldText, newText) {
@@ -3484,7 +4452,6 @@ function longestCommonSubsequence(a, b) {
3484
4452
  function formatDiff(diff, contextLines = 3) {
3485
4453
  const output = [];
3486
4454
  let lastPrintedIndex = -1;
3487
- let inHunk = false;
3488
4455
  const changeIndices = diff.map((d, i) => d.type !== "same" ? i : -1).filter((i) => i !== -1);
3489
4456
  if (changeIndices.length === 0) {
3490
4457
  return chalk14.gray(" (no changes)");
@@ -3515,7 +4482,7 @@ function getDiffStats(diff) {
3515
4482
  unchanged: diff.filter((d) => d.type === "same").length
3516
4483
  };
3517
4484
  }
3518
- async function diffCommand(blueprintId, options = {}) {
4485
+ async function diffCommand(fileOrId, options = {}) {
3519
4486
  console.log();
3520
4487
  console.log(chalk14.cyan("\u{1F431} LynxPrompt Diff"));
3521
4488
  console.log();
@@ -3524,14 +4491,90 @@ async function diffCommand(blueprintId, options = {}) {
3524
4491
  await diffLocal(cwd);
3525
4492
  return;
3526
4493
  }
3527
- if (!blueprintId) {
3528
- console.log(chalk14.red("\u2717 Please provide a blueprint ID to compare with."));
4494
+ const trackedFiles = await checkSyncStatus(cwd);
4495
+ if (fileOrId) {
4496
+ const tracked = await findBlueprintByFile(cwd, fileOrId);
4497
+ if (tracked) {
4498
+ await diffFileWithBlueprint(cwd, fileOrId, tracked.id);
4499
+ return;
4500
+ }
4501
+ await diffWithBlueprintId(cwd, fileOrId);
4502
+ return;
4503
+ }
4504
+ if (trackedFiles.length === 0) {
4505
+ console.log(chalk14.yellow("No tracked blueprints found."));
4506
+ console.log();
4507
+ console.log(chalk14.gray("To track a blueprint and compare changes:"));
4508
+ console.log(chalk14.gray(" 1. Pull a blueprint: lynxp pull <blueprint-id>"));
4509
+ console.log(chalk14.gray(" 2. Or link an existing file: lynxp link"));
3529
4510
  console.log();
3530
- console.log(chalk14.gray("Usage:"));
3531
- console.log(chalk14.gray(" lynxp diff <blueprint-id> Compare local with remote blueprint"));
3532
- console.log(chalk14.gray(" lynxp diff --local Compare .lynxprompt/rules/ with exports"));
4511
+ console.log(chalk14.gray("Other options:"));
4512
+ console.log(chalk14.gray(" lynxp diff --local Compare .lynxprompt/rules/ with exported files"));
3533
4513
  return;
3534
4514
  }
4515
+ let hasChanges = false;
4516
+ for (const { blueprint, localModified, fileExists: fileExists2 } of trackedFiles) {
4517
+ if (!fileExists2) {
4518
+ console.log(chalk14.red(`\u2717 ${blueprint.file} - file not found`));
4519
+ continue;
4520
+ }
4521
+ console.log(chalk14.cyan(`\u{1F4C4} ${blueprint.file}`));
4522
+ console.log(chalk14.gray(` Linked to: ${blueprint.name} (${blueprint.id})`));
4523
+ if (localModified) {
4524
+ hasChanges = true;
4525
+ await diffFileWithBlueprint(cwd, blueprint.file, blueprint.id, true);
4526
+ } else {
4527
+ console.log(chalk14.green(" \u2713 In sync with cloud"));
4528
+ }
4529
+ console.log();
4530
+ }
4531
+ if (!hasChanges) {
4532
+ console.log(chalk14.green("\u2713 All tracked files are in sync with their cloud blueprints!"));
4533
+ } else {
4534
+ console.log(chalk14.gray("To push local changes: lynxp push"));
4535
+ console.log(chalk14.gray("To pull cloud changes: lynxp pull <id>"));
4536
+ }
4537
+ console.log();
4538
+ }
4539
+ async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false) {
4540
+ const filePath = join11(cwd, file);
4541
+ if (!existsSync8(filePath)) {
4542
+ console.log(chalk14.red(`\u2717 File not found: ${file}`));
4543
+ return;
4544
+ }
4545
+ const spinner = compact ? null : ora11("Fetching blueprint...").start();
4546
+ try {
4547
+ const { blueprint } = await api.getBlueprint(blueprintId);
4548
+ spinner?.stop();
4549
+ if (!blueprint || !blueprint.content) {
4550
+ console.log(chalk14.red(`\u2717 Blueprint has no content`));
4551
+ return;
4552
+ }
4553
+ const localContent = await readFile9(filePath, "utf-8");
4554
+ const diff = computeDiff(blueprint.content, localContent);
4555
+ const stats = getDiffStats(diff);
4556
+ if (stats.added === 0 && stats.removed === 0) {
4557
+ if (!compact) {
4558
+ console.log(chalk14.green("\u2713 Files are identical!"));
4559
+ }
4560
+ } else {
4561
+ if (!compact) {
4562
+ console.log(chalk14.gray("Changes (cloud \u2192 local):"));
4563
+ console.log();
4564
+ }
4565
+ console.log(formatDiff(diff));
4566
+ console.log(chalk14.gray(` ${chalk14.green(`+${stats.added}`)} ${chalk14.red(`-${stats.removed}`)} lines`));
4567
+ }
4568
+ } catch (error) {
4569
+ spinner?.stop();
4570
+ if (error instanceof ApiRequestError) {
4571
+ console.log(chalk14.red(`\u2717 Could not fetch blueprint: ${error.message}`));
4572
+ } else {
4573
+ console.log(chalk14.red("\u2717 Failed to compare"));
4574
+ }
4575
+ }
4576
+ }
4577
+ async function diffWithBlueprintId(cwd, blueprintId) {
3535
4578
  if (!isAuthenticated()) {
3536
4579
  console.log(chalk14.yellow("\u26A0 Not logged in. Some blueprints may not be accessible."));
3537
4580
  console.log(chalk14.gray("Run 'lynxp login' to authenticate."));
@@ -3563,7 +4606,7 @@ async function diffCommand(blueprintId, options = {}) {
3563
4606
  const fullPath = join11(cwd, path2);
3564
4607
  if (existsSync8(fullPath)) {
3565
4608
  try {
3566
- localContent = await readFile10(fullPath, "utf-8");
4609
+ localContent = await readFile9(fullPath, "utf-8");
3567
4610
  localPath = path2;
3568
4611
  break;
3569
4612
  } catch {
@@ -3625,7 +4668,7 @@ async function diffLocal(cwd) {
3625
4668
  }
3626
4669
  let rulesContent;
3627
4670
  try {
3628
- rulesContent = await readFile10(rulesPath, "utf-8");
4671
+ rulesContent = await readFile9(rulesPath, "utf-8");
3629
4672
  } catch {
3630
4673
  console.log(chalk14.red("\u2717 Could not read .lynxprompt/rules/agents.md"));
3631
4674
  return;
@@ -3639,7 +4682,7 @@ async function diffLocal(cwd) {
3639
4682
  const filePath = join11(cwd, file.path);
3640
4683
  if (existsSync8(filePath)) {
3641
4684
  try {
3642
- const exportedContent = await readFile10(filePath, "utf-8");
4685
+ const exportedContent = await readFile9(filePath, "utf-8");
3643
4686
  let compareContent = exportedContent;
3644
4687
  if (file.path.endsWith(".mdc")) {
3645
4688
  const frontmatterEnd = exportedContent.indexOf("---", 3);
@@ -3691,33 +4734,53 @@ function getSourceFromVisibility2(visibility) {
3691
4734
  return "marketplace";
3692
4735
  }
3693
4736
  }
3694
- async function linkCommand(file, blueprintId, options = {}) {
4737
+ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
3695
4738
  const cwd = process.cwd();
3696
4739
  if (options.list) {
3697
4740
  await listTrackedBlueprints(cwd);
3698
4741
  return;
3699
4742
  }
3700
- if (!file) {
3701
- console.log(chalk15.red("\u2717 Please provide a file path to link."));
3702
- console.log();
3703
- console.log(chalk15.gray("Usage:"));
3704
- console.log(chalk15.gray(" lynxp link <file> <blueprint-id> Link a local file to a cloud blueprint"));
3705
- console.log(chalk15.gray(" lynxp link --list List all tracked blueprints"));
3706
- console.log();
3707
- console.log(chalk15.gray("Example:"));
3708
- console.log(chalk15.gray(" lynxp link AGENTS.md bp_abc123"));
3709
- return;
3710
- }
3711
- if (!blueprintId) {
3712
- console.log(chalk15.red("\u2717 Please provide a blueprint ID to link to."));
3713
- console.log();
3714
- console.log(chalk15.gray("Usage: lynxp link <file> <blueprint-id>"));
3715
- console.log(chalk15.gray("Example: lynxp link AGENTS.md bp_abc123"));
3716
- console.log();
3717
- console.log(chalk15.gray("To find blueprint IDs:"));
3718
- console.log(chalk15.gray(" lynxp list - Show your blueprints"));
3719
- console.log(chalk15.gray(" lynxp search <query> - Search marketplace"));
3720
- return;
4743
+ console.log();
4744
+ console.log(chalk15.cyan("\u{1F431} Link File to Blueprint"));
4745
+ console.log();
4746
+ let file;
4747
+ let blueprintId = blueprintIdArg;
4748
+ if (!fileArg) {
4749
+ const configFiles = [
4750
+ "AGENTS.md",
4751
+ "CLAUDE.md",
4752
+ ".cursor/rules/project.mdc",
4753
+ ".github/copilot-instructions.md",
4754
+ ".windsurfrules",
4755
+ ".zed/instructions.md",
4756
+ ".clinerules"
4757
+ ];
4758
+ const foundFiles = configFiles.filter((f) => existsSync9(join12(cwd, f)));
4759
+ if (foundFiles.length === 0) {
4760
+ console.log(chalk15.yellow("No AI configuration files found in this directory."));
4761
+ console.log();
4762
+ console.log(chalk15.gray("Create one first:"));
4763
+ console.log(chalk15.gray(" lynxp wizard Generate a new config file"));
4764
+ console.log(chalk15.gray(" lynxp pull <id> Download from marketplace"));
4765
+ return;
4766
+ }
4767
+ const { selectedFile } = await prompts7({
4768
+ type: "select",
4769
+ name: "selectedFile",
4770
+ message: "Which file do you want to link to a cloud blueprint?",
4771
+ choices: foundFiles.map((f) => ({
4772
+ title: f,
4773
+ value: f,
4774
+ description: "Local file exists"
4775
+ }))
4776
+ });
4777
+ if (!selectedFile) {
4778
+ console.log(chalk15.gray("Cancelled."));
4779
+ return;
4780
+ }
4781
+ file = selectedFile;
4782
+ } else {
4783
+ file = fileArg;
3721
4784
  }
3722
4785
  const filePath = join12(cwd, file);
3723
4786
  if (!existsSync9(filePath)) {
@@ -3726,7 +4789,9 @@ async function linkCommand(file, blueprintId, options = {}) {
3726
4789
  }
3727
4790
  const existing = await findBlueprintByFile(cwd, file);
3728
4791
  if (existing) {
3729
- console.log(chalk15.yellow(`\u26A0 This file is already linked to: ${existing.id}`));
4792
+ console.log(chalk15.yellow(`This file is already linked to: ${existing.name}`));
4793
+ console.log(chalk15.gray(` ID: ${existing.id}`));
4794
+ console.log();
3730
4795
  const { proceed } = await prompts7({
3731
4796
  type: "confirm",
3732
4797
  name: "proceed",
@@ -3738,11 +4803,120 @@ async function linkCommand(file, blueprintId, options = {}) {
3738
4803
  return;
3739
4804
  }
3740
4805
  }
3741
- if (!isAuthenticated()) {
3742
- console.log(
3743
- chalk15.yellow("Not logged in. Run 'lynxp login' to authenticate.")
3744
- );
3745
- process.exit(1);
4806
+ if (!blueprintId) {
4807
+ if (!isAuthenticated()) {
4808
+ console.log(chalk15.yellow("You need to login to access your blueprints."));
4809
+ const { doLogin } = await prompts7({
4810
+ type: "confirm",
4811
+ name: "doLogin",
4812
+ message: "Login now?",
4813
+ initial: true
4814
+ });
4815
+ if (doLogin) {
4816
+ console.log(chalk15.gray("Run 'lynxp login' in another terminal, then come back here."));
4817
+ return;
4818
+ }
4819
+ console.log(chalk15.gray("Cancelled."));
4820
+ return;
4821
+ }
4822
+ const { searchMethod } = await prompts7({
4823
+ type: "select",
4824
+ name: "searchMethod",
4825
+ message: "How do you want to find the blueprint?",
4826
+ choices: [
4827
+ { title: "\u{1F4CB} From my blueprints", value: "list" },
4828
+ { title: "\u{1F50D} Search marketplace", value: "search" },
4829
+ { title: "\u{1F522} Enter ID directly", value: "manual" }
4830
+ ]
4831
+ });
4832
+ if (!searchMethod) {
4833
+ console.log(chalk15.gray("Cancelled."));
4834
+ return;
4835
+ }
4836
+ if (searchMethod === "list") {
4837
+ const spinner2 = ora12("Fetching your blueprints...").start();
4838
+ try {
4839
+ const { blueprints } = await api.listBlueprints();
4840
+ spinner2.stop();
4841
+ if (!blueprints || blueprints.length === 0) {
4842
+ console.log(chalk15.yellow("You don't have any blueprints yet."));
4843
+ console.log(chalk15.gray("Create one with 'lynxp push' or search the marketplace."));
4844
+ return;
4845
+ }
4846
+ const { selected } = await prompts7({
4847
+ type: "select",
4848
+ name: "selected",
4849
+ message: "Select a blueprint:",
4850
+ choices: blueprints.map((b) => ({
4851
+ title: b.name,
4852
+ value: b.id,
4853
+ description: b.description?.substring(0, 50) || ""
4854
+ }))
4855
+ });
4856
+ if (!selected) {
4857
+ console.log(chalk15.gray("Cancelled."));
4858
+ return;
4859
+ }
4860
+ blueprintId = selected;
4861
+ } catch {
4862
+ spinner2.stop();
4863
+ console.log(chalk15.red("\u2717 Could not fetch blueprints"));
4864
+ return;
4865
+ }
4866
+ } else if (searchMethod === "search") {
4867
+ const { query } = await prompts7({
4868
+ type: "text",
4869
+ name: "query",
4870
+ message: "Search for:"
4871
+ });
4872
+ if (!query) {
4873
+ console.log(chalk15.gray("Cancelled."));
4874
+ return;
4875
+ }
4876
+ const spinner2 = ora12(`Searching for "${query}"...`).start();
4877
+ try {
4878
+ const results = await api.searchBlueprints(query, 10);
4879
+ spinner2.stop();
4880
+ if (!results.templates || results.templates.length === 0) {
4881
+ console.log(chalk15.yellow(`No blueprints found for "${query}"`));
4882
+ return;
4883
+ }
4884
+ const { selected } = await prompts7({
4885
+ type: "select",
4886
+ name: "selected",
4887
+ message: "Select a blueprint:",
4888
+ choices: results.templates.map((b) => ({
4889
+ title: `${b.name} (\u2605 ${b.likes})`,
4890
+ value: b.id,
4891
+ description: b.author ? `by ${b.author}` : ""
4892
+ }))
4893
+ });
4894
+ if (!selected) {
4895
+ console.log(chalk15.gray("Cancelled."));
4896
+ return;
4897
+ }
4898
+ blueprintId = selected;
4899
+ } catch {
4900
+ spinner2.stop();
4901
+ console.log(chalk15.red("\u2717 Search failed"));
4902
+ return;
4903
+ }
4904
+ } else {
4905
+ const { manualId } = await prompts7({
4906
+ type: "text",
4907
+ name: "manualId",
4908
+ message: "Enter blueprint ID:"
4909
+ });
4910
+ if (!manualId) {
4911
+ console.log(chalk15.gray("Cancelled."));
4912
+ return;
4913
+ }
4914
+ blueprintId = manualId;
4915
+ }
4916
+ }
4917
+ if (!blueprintId) {
4918
+ console.log(chalk15.red("\u2717 No blueprint ID provided."));
4919
+ return;
3746
4920
  }
3747
4921
  const spinner = ora12(`Fetching blueprint ${chalk15.cyan(blueprintId)}...`).start();
3748
4922
  try {
@@ -3778,11 +4952,10 @@ async function linkCommand(file, blueprintId, options = {}) {
3778
4952
  console.log(chalk15.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
3779
4953
  console.log();
3780
4954
  console.log(chalk15.gray("Next steps:"));
3781
- console.log(chalk15.gray(` \u2022 Run 'lynxp pull ${blueprintId}' to update local file from cloud`));
3782
- console.log(chalk15.gray(` \u2022 Run 'lynxp diff ${blueprintId}' to see differences`));
4955
+ console.log(chalk15.gray(` \u2022 Run 'lynxp diff' to see differences`));
3783
4956
  console.log(chalk15.gray(` \u2022 Run 'lynxp status' to see all tracked blueprints`));
3784
4957
  if (!isMarketplace) {
3785
- console.log(chalk15.gray(` \u2022 Run 'lynxp push ${file}' to push local changes to cloud`));
4958
+ console.log(chalk15.gray(` \u2022 Run 'lynxp push' to push local changes to cloud`));
3786
4959
  }
3787
4960
  console.log();
3788
4961
  } catch (error) {
@@ -3801,23 +4974,43 @@ async function linkCommand(file, blueprintId, options = {}) {
3801
4974
  }
3802
4975
  }
3803
4976
  }
3804
- async function unlinkCommand(file) {
4977
+ async function unlinkCommand(fileArg) {
3805
4978
  const cwd = process.cwd();
3806
- if (!file) {
3807
- console.log(chalk15.red("\u2717 Please provide a file path to unlink."));
3808
- console.log();
3809
- console.log(chalk15.gray("Usage: lynxp unlink <file>"));
3810
- console.log(chalk15.gray("Example: lynxp unlink AGENTS.md"));
3811
- return;
4979
+ console.log();
4980
+ console.log(chalk15.cyan("\u{1F431} Unlink File from Blueprint"));
4981
+ console.log();
4982
+ let file;
4983
+ if (!fileArg) {
4984
+ const status = await checkSyncStatus(cwd);
4985
+ if (status.length === 0) {
4986
+ console.log(chalk15.yellow("No files are currently linked to blueprints."));
4987
+ return;
4988
+ }
4989
+ const { selectedFile } = await prompts7({
4990
+ type: "select",
4991
+ name: "selectedFile",
4992
+ message: "Which file do you want to unlink?",
4993
+ choices: status.map(({ blueprint }) => ({
4994
+ title: blueprint.file,
4995
+ value: blueprint.file,
4996
+ description: `${blueprint.name} (${blueprint.source})`
4997
+ }))
4998
+ });
4999
+ if (!selectedFile) {
5000
+ console.log(chalk15.gray("Cancelled."));
5001
+ return;
5002
+ }
5003
+ file = selectedFile;
5004
+ } else {
5005
+ file = fileArg;
3812
5006
  }
3813
5007
  const tracked = await findBlueprintByFile(cwd, file);
3814
5008
  if (!tracked) {
3815
- console.log(chalk15.yellow(`\u26A0 File is not linked to any blueprint: ${file}`));
5009
+ console.log(chalk15.yellow(`File is not linked to any blueprint: ${file}`));
3816
5010
  return;
3817
5011
  }
3818
- console.log();
3819
- console.log(chalk15.cyan(`Currently linked to: ${tracked.id}`));
3820
- console.log(chalk15.gray(` Name: ${tracked.name}`));
5012
+ console.log(chalk15.gray(`Currently linked to: ${tracked.name}`));
5013
+ console.log(chalk15.gray(` ID: ${tracked.id}`));
3821
5014
  console.log(chalk15.gray(` Source: ${tracked.source}`));
3822
5015
  console.log();
3823
5016
  const { confirm } = await prompts7({
@@ -3834,8 +5027,7 @@ async function unlinkCommand(file) {
3834
5027
  if (success) {
3835
5028
  console.log();
3836
5029
  console.log(chalk15.green(`\u2705 Unlinked: ${file}`));
3837
- console.log(chalk15.gray(" The file is now a standalone local file."));
3838
- console.log(chalk15.gray(" It will no longer receive updates from the cloud blueprint."));
5030
+ console.log(chalk15.gray(" The file is now standalone. Changes won't sync with the cloud."));
3839
5031
  console.log();
3840
5032
  } else {
3841
5033
  console.log(chalk15.red("\u2717 Failed to unlink file."));
@@ -3851,7 +5043,7 @@ async function listTrackedBlueprints(cwd) {
3851
5043
  console.log();
3852
5044
  console.log(chalk15.gray("To track a blueprint:"));
3853
5045
  console.log(chalk15.gray(" lynxp pull <blueprint-id> Download and track a blueprint"));
3854
- console.log(chalk15.gray(" lynxp link <file> <id> Link an existing file to a blueprint"));
5046
+ console.log(chalk15.gray(" lynxp link Link an existing file to a blueprint"));
3855
5047
  return;
3856
5048
  }
3857
5049
  for (const { blueprint, localModified, fileExists: fileExists2 } of status) {
@@ -3888,8 +5080,8 @@ program.command("search <query>").description("Search public blueprints in the m
3888
5080
  program.command("list").description("List your blueprints").option("-l, --limit <number>", "Number of results", "20").option("-v, --visibility <visibility>", "Filter: PRIVATE, TEAM, PUBLIC, or all").action(listCommand);
3889
5081
  program.command("push [file]").description("Push local file to LynxPrompt cloud as a blueprint").option("-n, --name <name>", "Blueprint name").option("-d, --description <desc>", "Blueprint description").option("-v, --visibility <vis>", "Visibility: PRIVATE, TEAM, or PUBLIC", "PRIVATE").option("-t, --tags <tags>", "Tags (comma-separated)").option("-y, --yes", "Skip prompts").action(pushCommand);
3890
5082
  program.command("link [file] [blueprint-id]").description("Link a local file to a cloud blueprint for tracking").option("--list", "List all tracked blueprints").action(linkCommand);
3891
- program.command("unlink <file>").description("Disconnect a local file from its cloud blueprint").action(unlinkCommand);
3892
- program.command("diff [blueprint-id]").description("Show changes between local and remote blueprint").option("--local", "Compare .lynxprompt/rules/ with exported files").action(diffCommand);
5083
+ program.command("unlink [file]").description("Disconnect a local file from its cloud blueprint").action(unlinkCommand);
5084
+ program.command("diff [file-or-id]").description("Compare tracked files with their cloud blueprints").option("--local", "Compare .lynxprompt/rules/ with exported files").action(diffCommand);
3893
5085
  program.command("init").description("Initialize .lynxprompt/ for multi-editor sync (advanced)").option("-y, --yes", "Skip prompts and use defaults").option("-f, --force", "Re-initialize even if already initialized").action(initCommand);
3894
5086
  program.command("sync").description("Sync .lynxprompt/rules/ to all configured agents").option("--dry-run", "Preview changes without writing files").option("-f, --force", "Skip prompts (for CI/automation)").action(syncCommand);
3895
5087
  program.command("agents [action] [agent]").description("Manage AI agents (list, enable, disable, detect)").option("-i, --interactive", "Interactive agent selection").action(agentsCommand);