lynxprompt 0.1.1 → 0.2.1

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk12 from "chalk";
5
+ import chalk15 from "chalk";
6
6
 
7
7
  // src/commands/login.ts
8
8
  import chalk from "chalk";
@@ -408,67 +408,310 @@ function handleApiError(error) {
408
408
  import chalk5 from "chalk";
409
409
  import ora4 from "ora";
410
410
  import prompts from "prompts";
411
- import { writeFile, access, mkdir } from "fs/promises";
411
+ import { writeFile as writeFile2, mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
412
+ import { join as join2, dirname as dirname2 } from "path";
413
+ import { existsSync } from "fs";
414
+
415
+ // src/utils/blueprint-tracker.ts
416
+ import { readFile, writeFile, mkdir, access } from "fs/promises";
412
417
  import { join, dirname } from "path";
418
+ import { createHash } from "crypto";
419
+ import * as yaml from "yaml";
420
+ var BLUEPRINTS_FILE = ".lynxprompt/blueprints.yml";
421
+ function calculateChecksum(content) {
422
+ return createHash("sha256").update(content).digest("hex").substring(0, 16);
423
+ }
424
+ async function loadBlueprints(cwd) {
425
+ const filePath = join(cwd, BLUEPRINTS_FILE);
426
+ try {
427
+ await access(filePath);
428
+ const content = await readFile(filePath, "utf-8");
429
+ const config2 = yaml.parse(content);
430
+ return config2 || { version: "1", blueprints: [] };
431
+ } catch {
432
+ return { version: "1", blueprints: [] };
433
+ }
434
+ }
435
+ async function saveBlueprints(cwd, config2) {
436
+ const filePath = join(cwd, BLUEPRINTS_FILE);
437
+ const dir = dirname(filePath);
438
+ await mkdir(dir, { recursive: true });
439
+ const content = yaml.stringify(config2, {
440
+ lineWidth: 0,
441
+ singleQuote: false
442
+ });
443
+ await writeFile(filePath, content, "utf-8");
444
+ }
445
+ async function trackBlueprint(cwd, blueprint) {
446
+ const config2 = await loadBlueprints(cwd);
447
+ config2.blueprints = config2.blueprints.filter((b) => b.file !== blueprint.file);
448
+ const editable = blueprint.source !== "marketplace";
449
+ const canPull = true;
450
+ config2.blueprints.push({
451
+ id: blueprint.id,
452
+ source: blueprint.source,
453
+ file: blueprint.file,
454
+ name: blueprint.name,
455
+ pulledAt: (/* @__PURE__ */ new Date()).toISOString(),
456
+ checksum: calculateChecksum(blueprint.content),
457
+ version: blueprint.version,
458
+ editable,
459
+ canPull
460
+ });
461
+ await saveBlueprints(cwd, config2);
462
+ }
463
+ async function findBlueprintByFile(cwd, file) {
464
+ const config2 = await loadBlueprints(cwd);
465
+ return config2.blueprints.find((b) => b.file === file) || null;
466
+ }
467
+ async function hasLocalChanges(cwd, tracked) {
468
+ try {
469
+ const filePath = join(cwd, tracked.file);
470
+ const content = await readFile(filePath, "utf-8");
471
+ const currentChecksum = calculateChecksum(content);
472
+ return currentChecksum !== tracked.checksum;
473
+ } catch {
474
+ return false;
475
+ }
476
+ }
477
+ async function untrackBlueprint(cwd, file) {
478
+ const config2 = await loadBlueprints(cwd);
479
+ const initialCount = config2.blueprints.length;
480
+ config2.blueprints = config2.blueprints.filter((b) => b.file !== file);
481
+ if (config2.blueprints.length < initialCount) {
482
+ await saveBlueprints(cwd, config2);
483
+ return true;
484
+ }
485
+ return false;
486
+ }
487
+ async function linkBlueprint(cwd, file, blueprintId, blueprintName, source) {
488
+ try {
489
+ const filePath = join(cwd, file);
490
+ const content = await readFile(filePath, "utf-8");
491
+ await trackBlueprint(cwd, {
492
+ id: blueprintId,
493
+ name: blueprintName,
494
+ file,
495
+ content,
496
+ source
497
+ });
498
+ } catch (error) {
499
+ throw new Error(`Could not read file ${file} to link`);
500
+ }
501
+ }
502
+ async function checkSyncStatus(cwd) {
503
+ const config2 = await loadBlueprints(cwd);
504
+ const results = [];
505
+ for (const blueprint of config2.blueprints) {
506
+ const filePath = join(cwd, blueprint.file);
507
+ let fileExists2 = false;
508
+ let localModified = false;
509
+ try {
510
+ await access(filePath);
511
+ fileExists2 = true;
512
+ localModified = await hasLocalChanges(cwd, blueprint);
513
+ } catch {
514
+ fileExists2 = false;
515
+ }
516
+ results.push({ blueprint, localModified, fileExists: fileExists2 });
517
+ }
518
+ return results;
519
+ }
520
+
521
+ // src/commands/pull.ts
413
522
  var TYPE_TO_FILENAME = {
414
523
  AGENTS_MD: "AGENTS.md",
415
- CURSOR_RULES: ".cursorrules",
524
+ CURSOR_RULES: ".cursor/rules/project.mdc",
416
525
  COPILOT_INSTRUCTIONS: ".github/copilot-instructions.md",
417
526
  WINDSURF_RULES: ".windsurfrules",
418
527
  ZED_INSTRUCTIONS: ".zed/instructions.md",
419
528
  CLAUDE_MD: "CLAUDE.md",
420
529
  GENERIC: "ai-config.md"
421
530
  };
531
+ function getSourceFromVisibility(visibility) {
532
+ switch (visibility) {
533
+ case "PUBLIC":
534
+ return "marketplace";
535
+ case "TEAM":
536
+ return "team";
537
+ case "PRIVATE":
538
+ return "private";
539
+ default:
540
+ return "marketplace";
541
+ }
542
+ }
422
543
  async function pullCommand(id, options) {
423
544
  if (!isAuthenticated()) {
424
545
  console.log(
425
- chalk5.yellow("Not logged in. Run 'lynxprompt login' to authenticate.")
546
+ chalk5.yellow("Not logged in. Run 'lynxp login' to authenticate.")
426
547
  );
427
548
  process.exit(1);
428
549
  }
550
+ const cwd = process.cwd();
429
551
  const spinner = ora4(`Fetching blueprint ${chalk5.cyan(id)}...`).start();
430
552
  try {
431
553
  const { blueprint } = await api.getBlueprint(id);
432
554
  spinner.stop();
433
555
  if (!blueprint.content) {
434
- console.error(chalk5.red("Blueprint has no content."));
556
+ console.error(chalk5.red("\u2717 Blueprint has no content."));
435
557
  process.exit(1);
436
558
  }
559
+ const source = getSourceFromVisibility(blueprint.visibility);
560
+ const isMarketplace = source === "marketplace";
561
+ console.log();
562
+ console.log(chalk5.cyan(`\u{1F431} Blueprint: ${chalk5.bold(blueprint.name)}`));
563
+ if (blueprint.description) {
564
+ console.log(chalk5.gray(` ${blueprint.description}`));
565
+ }
566
+ console.log(chalk5.gray(` Type: ${blueprint.type} \u2022 Tier: ${blueprint.tier} \u2022 Visibility: ${blueprint.visibility}`));
567
+ if (isMarketplace) {
568
+ console.log(chalk5.yellow(` \u{1F4E6} Marketplace blueprint (read-only - changes won't sync back)`));
569
+ } else if (source === "team") {
570
+ console.log(chalk5.blue(` \u{1F465} Team blueprint (can sync changes)`));
571
+ } else if (source === "private") {
572
+ console.log(chalk5.green(` \u{1F512} Private blueprint (can sync changes)`));
573
+ }
574
+ console.log();
575
+ if (options.preview) {
576
+ console.log(chalk5.gray("\u2500".repeat(60)));
577
+ console.log();
578
+ const lines = blueprint.content.split("\n");
579
+ const previewLines = lines.slice(0, 50);
580
+ for (const line of previewLines) {
581
+ if (line.startsWith("#")) {
582
+ console.log(chalk5.cyan(line));
583
+ } else if (line.startsWith(">")) {
584
+ console.log(chalk5.gray(line));
585
+ } else if (line.startsWith("- ") || line.startsWith("* ")) {
586
+ console.log(chalk5.white(line));
587
+ } else if (line.startsWith("```")) {
588
+ console.log(chalk5.yellow(line));
589
+ } else {
590
+ console.log(line);
591
+ }
592
+ }
593
+ if (lines.length > 50) {
594
+ console.log();
595
+ console.log(chalk5.gray(`... ${lines.length - 50} more lines`));
596
+ }
597
+ console.log();
598
+ console.log(chalk5.gray("\u2500".repeat(60)));
599
+ console.log();
600
+ console.log(chalk5.gray("Run without --preview to download this blueprint."));
601
+ return;
602
+ }
437
603
  const filename = TYPE_TO_FILENAME[blueprint.type] || "ai-config.md";
438
- const outputPath = join(options.output, filename);
439
- let fileExists2 = false;
440
- try {
441
- await access(outputPath);
442
- fileExists2 = true;
443
- } catch {
604
+ const outputPath = join2(options.output, filename);
605
+ let localContent = null;
606
+ if (existsSync(outputPath)) {
607
+ try {
608
+ localContent = await readFile2(outputPath, "utf-8");
609
+ } catch {
610
+ }
444
611
  }
445
- if (fileExists2 && !options.yes) {
612
+ const existingTracked = await findBlueprintByFile(cwd, filename);
613
+ if (existingTracked && existingTracked.id !== id) {
614
+ console.log(chalk5.yellow(`\u26A0 This file is already linked to a different blueprint: ${existingTracked.id}`));
615
+ if (!options.yes) {
616
+ const { proceed } = await prompts({
617
+ type: "confirm",
618
+ name: "proceed",
619
+ message: "Replace the link with the new blueprint?",
620
+ initial: false
621
+ });
622
+ if (!proceed) {
623
+ console.log(chalk5.gray("Cancelled."));
624
+ return;
625
+ }
626
+ }
627
+ }
628
+ if (localContent && !options.yes) {
629
+ const localLines = localContent.split("\n").length;
630
+ const remoteLines = blueprint.content.split("\n").length;
631
+ console.log(chalk5.yellow(`\u26A0 File exists: ${outputPath}`));
632
+ console.log(chalk5.gray(` Local: ${localLines} lines`));
633
+ console.log(chalk5.gray(` Remote: ${remoteLines} lines`));
634
+ console.log();
446
635
  const response = await prompts({
447
- type: "confirm",
448
- name: "overwrite",
449
- message: `File ${chalk5.cyan(outputPath)} already exists. Overwrite?`,
450
- initial: false
636
+ type: "select",
637
+ name: "action",
638
+ message: "What would you like to do?",
639
+ choices: [
640
+ { title: "Overwrite with remote version", value: "overwrite" },
641
+ { title: "Preview remote content first", value: "preview" },
642
+ { title: "Cancel", value: "cancel" }
643
+ ]
451
644
  });
452
- if (!response.overwrite) {
453
- console.log(chalk5.yellow("Aborted."));
645
+ if (response.action === "cancel" || !response.action) {
646
+ console.log(chalk5.gray("Cancelled."));
454
647
  return;
455
648
  }
649
+ if (response.action === "preview") {
650
+ console.log();
651
+ console.log(chalk5.gray("\u2500".repeat(60)));
652
+ console.log();
653
+ const lines = blueprint.content.split("\n").slice(0, 30);
654
+ for (const line of lines) {
655
+ if (line.startsWith("#")) {
656
+ console.log(chalk5.cyan(line));
657
+ } else {
658
+ console.log(line);
659
+ }
660
+ }
661
+ if (blueprint.content.split("\n").length > 30) {
662
+ console.log(chalk5.gray(`... ${blueprint.content.split("\n").length - 30} more lines`));
663
+ }
664
+ console.log();
665
+ console.log(chalk5.gray("\u2500".repeat(60)));
666
+ console.log();
667
+ const confirmResponse = await prompts({
668
+ type: "confirm",
669
+ name: "confirm",
670
+ message: "Download and overwrite local file?",
671
+ initial: false
672
+ });
673
+ if (!confirmResponse.confirm) {
674
+ console.log(chalk5.gray("Cancelled."));
675
+ return;
676
+ }
677
+ }
456
678
  }
457
- const dir = dirname(outputPath);
679
+ const dir = dirname2(outputPath);
458
680
  if (dir !== ".") {
459
- await mkdir(dir, { recursive: true });
681
+ await mkdir2(dir, { recursive: true });
682
+ }
683
+ await writeFile2(outputPath, blueprint.content, "utf-8");
684
+ if (options.track !== false) {
685
+ await trackBlueprint(cwd, {
686
+ id: blueprint.id,
687
+ name: blueprint.name,
688
+ file: filename,
689
+ content: blueprint.content,
690
+ source
691
+ });
692
+ }
693
+ console.log(chalk5.green(`\u2705 Downloaded: ${chalk5.bold(outputPath)}`));
694
+ if (options.track !== false) {
695
+ console.log(chalk5.gray(` Linked to: ${blueprint.id}`));
696
+ if (isMarketplace) {
697
+ console.log(chalk5.gray(` Updates: Run 'lynxp pull ${id}' to sync updates`));
698
+ } else {
699
+ console.log(chalk5.gray(` Sync: Run 'lynxp push ${filename}' to push changes`));
700
+ }
460
701
  }
461
- await writeFile(outputPath, blueprint.content, "utf-8");
462
- console.log();
463
- console.log(chalk5.green(`\u2705 Downloaded ${chalk5.bold(blueprint.name)}`));
464
- console.log(` ${chalk5.gray("File:")} ${chalk5.cyan(outputPath)}`);
465
- console.log(` ${chalk5.gray("Type:")} ${blueprint.type}`);
466
- console.log(` ${chalk5.gray("Tier:")} ${blueprint.tier}`);
467
702
  console.log();
468
703
  const editorHint = getEditorHint(blueprint.type);
469
704
  if (editorHint) {
470
705
  console.log(chalk5.gray(`\u{1F4A1} ${editorHint}`));
706
+ console.log();
707
+ }
708
+ console.log(chalk5.gray("Tips:"));
709
+ console.log(chalk5.gray(` \u2022 Run 'lynxp status' to see tracked blueprints`));
710
+ console.log(chalk5.gray(` \u2022 Run 'lynxp diff ${id}' to see changes between local and remote`));
711
+ if (isMarketplace) {
712
+ console.log(chalk5.gray(` \u2022 Run 'lynxp unlink ${filename}' to disconnect and make editable`));
471
713
  }
714
+ console.log();
472
715
  } catch (error) {
473
716
  spinner.fail("Failed to pull blueprint");
474
717
  handleApiError2(error);
@@ -496,29 +739,29 @@ function handleApiError2(error) {
496
739
  if (error instanceof ApiRequestError) {
497
740
  if (error.statusCode === 401) {
498
741
  console.error(
499
- chalk5.red("Your session has expired. Please run 'lynxprompt login' again.")
742
+ chalk5.red("\u2717 Your session has expired. Please run 'lynxp login' again.")
500
743
  );
501
744
  } else if (error.statusCode === 403) {
502
745
  console.error(
503
- chalk5.red("You don't have access to this blueprint.")
746
+ chalk5.red("\u2717 You don't have access to this blueprint.")
504
747
  );
505
748
  console.error(
506
749
  chalk5.gray(
507
- "This might be a private blueprint or require a higher subscription tier."
750
+ " This might be a private blueprint or require a higher subscription tier."
508
751
  )
509
752
  );
510
753
  } else if (error.statusCode === 404) {
511
- console.error(chalk5.red("Blueprint not found."));
754
+ console.error(chalk5.red("\u2717 Blueprint not found."));
512
755
  console.error(
513
756
  chalk5.gray(
514
- "Make sure you have the correct blueprint ID. Use 'lynxprompt list' to see your blueprints."
757
+ " Make sure you have the correct blueprint ID. Use 'lynxp list' to see your blueprints."
515
758
  )
516
759
  );
517
760
  } else {
518
- console.error(chalk5.red(`Error: ${error.message}`));
761
+ console.error(chalk5.red(`\u2717 Error: ${error.message}`));
519
762
  }
520
763
  } else {
521
- console.error(chalk5.red("An unexpected error occurred."));
764
+ console.error(chalk5.red("\u2717 An unexpected error occurred."));
522
765
  }
523
766
  process.exit(1);
524
767
  }
@@ -527,14 +770,14 @@ function handleApiError2(error) {
527
770
  import chalk6 from "chalk";
528
771
  import prompts2 from "prompts";
529
772
  import ora5 from "ora";
530
- import { writeFile as writeFile2, mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
531
- import { join as join4, dirname as dirname2, basename } from "path";
532
- import { existsSync as existsSync2 } from "fs";
533
- import * as yaml from "yaml";
773
+ import { writeFile as writeFile3, mkdir as mkdir3, readFile as readFile4 } from "fs/promises";
774
+ import { join as join5, dirname as dirname3, basename } from "path";
775
+ import { existsSync as existsSync3 } from "fs";
776
+ import * as yaml2 from "yaml";
534
777
 
535
778
  // src/utils/agent-detector.ts
536
- import { existsSync, readdirSync, readFileSync, statSync } from "fs";
537
- import { join as join2 } from "path";
779
+ import { existsSync as existsSync2, readdirSync, readFileSync, statSync } from "fs";
780
+ import { join as join3 } from "path";
538
781
 
539
782
  // src/utils/agents.ts
540
783
  var AGENTS = [
@@ -832,8 +1075,8 @@ function detectAgent(cwd, agent) {
832
1075
  let hasContent = false;
833
1076
  let ruleCount = 0;
834
1077
  for (const pattern of agent.patterns) {
835
- const fullPath = join2(cwd, pattern);
836
- if (!existsSync(fullPath)) {
1078
+ const fullPath = join3(cwd, pattern);
1079
+ if (!existsSync2(fullPath)) {
837
1080
  continue;
838
1081
  }
839
1082
  const stats = statSync(fullPath);
@@ -867,7 +1110,7 @@ function scanDirectory(dirPath, format) {
867
1110
  const name = entry.name.toLowerCase();
868
1111
  const isRuleFile = format === "mdc" && name.endsWith(".mdc") || format === "markdown" && name.endsWith(".md") || format === "json" && name.endsWith(".json") || format === "yaml" && (name.endsWith(".yml") || name.endsWith(".yaml"));
869
1112
  if (!isRuleFile) continue;
870
- const filePath = join2(dirPath, entry.name);
1113
+ const filePath = join3(dirPath, entry.name);
871
1114
  const content = safeReadFile(filePath);
872
1115
  if (content && content.trim().length > 0) {
873
1116
  results.push({
@@ -915,130 +1158,230 @@ function formatDetectionResults(result) {
915
1158
  }
916
1159
 
917
1160
  // src/utils/detect.ts
918
- import { readFile, access as access2 } from "fs/promises";
919
- import { join as join3 } from "path";
1161
+ import { readFile as readFile3, access as access3 } from "fs/promises";
1162
+ import { join as join4 } from "path";
1163
+ var JS_FRAMEWORK_PATTERNS = {
1164
+ nextjs: ["next"],
1165
+ react: ["react", "react-dom"],
1166
+ vue: ["vue"],
1167
+ angular: ["@angular/core"],
1168
+ svelte: ["svelte", "@sveltejs/kit"],
1169
+ solid: ["solid-js"],
1170
+ remix: ["@remix-run/react"],
1171
+ astro: ["astro"],
1172
+ nuxt: ["nuxt"],
1173
+ gatsby: ["gatsby"]
1174
+ };
1175
+ var JS_TOOL_PATTERNS = {
1176
+ typescript: ["typescript"],
1177
+ tailwind: ["tailwindcss"],
1178
+ prisma: ["prisma", "@prisma/client"],
1179
+ drizzle: ["drizzle-orm"],
1180
+ express: ["express"],
1181
+ fastify: ["fastify"],
1182
+ hono: ["hono"],
1183
+ elysia: ["elysia"],
1184
+ trpc: ["@trpc/server"],
1185
+ graphql: ["graphql", "@apollo/server"],
1186
+ jest: ["jest"],
1187
+ vitest: ["vitest"],
1188
+ playwright: ["@playwright/test"],
1189
+ cypress: ["cypress"],
1190
+ eslint: ["eslint"],
1191
+ biome: ["@biomejs/biome"],
1192
+ prettier: ["prettier"],
1193
+ vite: ["vite"],
1194
+ webpack: ["webpack"],
1195
+ turbo: ["turbo"]
1196
+ };
920
1197
  async function detectProject(cwd) {
921
1198
  const detected = {
922
1199
  name: null,
923
1200
  stack: [],
924
1201
  commands: {},
925
- packageManager: null
1202
+ packageManager: null,
1203
+ type: "unknown"
926
1204
  };
927
- const packageJsonPath = join3(cwd, "package.json");
1205
+ const packageJsonPath = join4(cwd, "package.json");
928
1206
  if (await fileExists(packageJsonPath)) {
929
1207
  try {
930
- const content = await readFile(packageJsonPath, "utf-8");
1208
+ const content = await readFile3(packageJsonPath, "utf-8");
931
1209
  const pkg = JSON.parse(content);
932
1210
  detected.name = pkg.name || null;
1211
+ detected.description = pkg.description;
1212
+ if (pkg.workspaces || await fileExists(join4(cwd, "pnpm-workspace.yaml"))) {
1213
+ detected.type = "monorepo";
1214
+ } else if (pkg.main || pkg.exports) {
1215
+ detected.type = "library";
1216
+ } else {
1217
+ detected.type = "application";
1218
+ }
933
1219
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
934
- if (allDeps["typescript"]) detected.stack.push("typescript");
935
- if (allDeps["react"]) detected.stack.push("react");
936
- if (allDeps["next"]) detected.stack.push("nextjs");
937
- if (allDeps["vue"]) detected.stack.push("vue");
938
- if (allDeps["@angular/core"]) detected.stack.push("angular");
939
- if (allDeps["svelte"]) detected.stack.push("svelte");
940
- if (allDeps["express"]) detected.stack.push("express");
941
- if (allDeps["fastify"]) detected.stack.push("fastify");
942
- if (allDeps["prisma"]) detected.stack.push("prisma");
943
- if (allDeps["tailwindcss"]) detected.stack.push("tailwind");
1220
+ for (const [framework, deps] of Object.entries(JS_FRAMEWORK_PATTERNS)) {
1221
+ if (deps.some((dep) => allDeps[dep])) {
1222
+ detected.stack.push(framework);
1223
+ }
1224
+ }
1225
+ for (const [tool, deps] of Object.entries(JS_TOOL_PATTERNS)) {
1226
+ if (deps.some((dep) => allDeps[dep])) {
1227
+ detected.stack.push(tool);
1228
+ }
1229
+ }
1230
+ if (detected.stack.length === 0 || detected.stack.length === 1 && detected.stack[0] === "typescript") {
1231
+ detected.stack.unshift("javascript");
1232
+ }
944
1233
  if (pkg.scripts) {
945
1234
  detected.commands.build = pkg.scripts.build;
946
1235
  detected.commands.test = pkg.scripts.test;
947
- detected.commands.lint = pkg.scripts.lint;
948
- detected.commands.dev = pkg.scripts.dev || pkg.scripts.start;
1236
+ detected.commands.lint = pkg.scripts.lint || pkg.scripts["lint:check"];
1237
+ detected.commands.dev = pkg.scripts.dev || pkg.scripts.start || pkg.scripts.serve;
1238
+ detected.commands.format = pkg.scripts.format || pkg.scripts.prettier;
949
1239
  }
950
- if (await fileExists(join3(cwd, "pnpm-lock.yaml"))) {
1240
+ if (await fileExists(join4(cwd, "pnpm-lock.yaml"))) {
951
1241
  detected.packageManager = "pnpm";
952
- } else if (await fileExists(join3(cwd, "yarn.lock"))) {
1242
+ } else if (await fileExists(join4(cwd, "yarn.lock"))) {
953
1243
  detected.packageManager = "yarn";
954
- } else if (await fileExists(join3(cwd, "bun.lockb"))) {
1244
+ } else if (await fileExists(join4(cwd, "bun.lockb"))) {
955
1245
  detected.packageManager = "bun";
956
- } else if (await fileExists(join3(cwd, "package-lock.json"))) {
1246
+ } else if (await fileExists(join4(cwd, "package-lock.json"))) {
957
1247
  detected.packageManager = "npm";
958
1248
  }
1249
+ if (detected.packageManager && detected.packageManager !== "npm") {
1250
+ const pm = detected.packageManager;
1251
+ for (const [key, value] of Object.entries(detected.commands)) {
1252
+ if (value && !value.startsWith(pm) && !value.startsWith("npx")) {
1253
+ detected.commands[key] = `${pm} run ${value}`;
1254
+ }
1255
+ }
1256
+ } else if (detected.commands) {
1257
+ for (const [key, value] of Object.entries(detected.commands)) {
1258
+ if (value && !value.startsWith("npm") && !value.startsWith("npx")) {
1259
+ detected.commands[key] = `npm run ${value}`;
1260
+ }
1261
+ }
1262
+ }
1263
+ if (pkg.scripts) {
1264
+ detected.commands.build = pkg.scripts.build ? "build" : void 0;
1265
+ detected.commands.test = pkg.scripts.test ? "test" : void 0;
1266
+ detected.commands.lint = pkg.scripts.lint ? "lint" : pkg.scripts["lint:check"] ? "lint:check" : void 0;
1267
+ detected.commands.dev = pkg.scripts.dev ? "dev" : pkg.scripts.start ? "start" : pkg.scripts.serve ? "serve" : void 0;
1268
+ }
959
1269
  return detected;
960
1270
  } catch {
961
1271
  }
962
1272
  }
963
- const pyprojectPath = join3(cwd, "pyproject.toml");
1273
+ const pyprojectPath = join4(cwd, "pyproject.toml");
964
1274
  if (await fileExists(pyprojectPath)) {
965
1275
  try {
966
- const content = await readFile(pyprojectPath, "utf-8");
1276
+ const content = await readFile3(pyprojectPath, "utf-8");
967
1277
  detected.stack.push("python");
1278
+ detected.type = "application";
968
1279
  const nameMatch = content.match(/name\s*=\s*"([^"]+)"/);
969
1280
  if (nameMatch) detected.name = nameMatch[1];
970
1281
  if (content.includes("fastapi")) detected.stack.push("fastapi");
971
1282
  if (content.includes("django")) detected.stack.push("django");
972
1283
  if (content.includes("flask")) detected.stack.push("flask");
1284
+ if (content.includes("pydantic")) detected.stack.push("pydantic");
1285
+ if (content.includes("sqlalchemy")) detected.stack.push("sqlalchemy");
1286
+ if (content.includes("pytest")) detected.stack.push("pytest");
1287
+ if (content.includes("ruff")) detected.stack.push("ruff");
1288
+ if (content.includes("mypy")) detected.stack.push("mypy");
973
1289
  detected.commands.test = "pytest";
974
- detected.commands.lint = "ruff check";
1290
+ detected.commands.lint = "ruff check .";
1291
+ if (content.includes("[tool.poetry]")) {
1292
+ detected.packageManager = "yarn";
1293
+ detected.commands.dev = "poetry run python -m uvicorn main:app --reload";
1294
+ } else if (await fileExists(join4(cwd, "uv.lock"))) {
1295
+ detected.commands.dev = "uv run python main.py";
1296
+ }
975
1297
  return detected;
976
1298
  } catch {
977
1299
  }
978
1300
  }
979
- const requirementsPath = join3(cwd, "requirements.txt");
1301
+ const requirementsPath = join4(cwd, "requirements.txt");
980
1302
  if (await fileExists(requirementsPath)) {
981
1303
  try {
982
- const content = await readFile(requirementsPath, "utf-8");
1304
+ const content = await readFile3(requirementsPath, "utf-8");
983
1305
  detected.stack.push("python");
984
- if (content.includes("fastapi")) detected.stack.push("fastapi");
985
- if (content.includes("django")) detected.stack.push("django");
986
- if (content.includes("flask")) detected.stack.push("flask");
1306
+ detected.type = "application";
1307
+ if (content.toLowerCase().includes("fastapi")) detected.stack.push("fastapi");
1308
+ if (content.toLowerCase().includes("django")) detected.stack.push("django");
1309
+ if (content.toLowerCase().includes("flask")) detected.stack.push("flask");
987
1310
  detected.commands.test = "pytest";
988
- detected.commands.lint = "ruff check";
1311
+ detected.commands.lint = "ruff check .";
989
1312
  return detected;
990
1313
  } catch {
991
1314
  }
992
1315
  }
993
- const cargoPath = join3(cwd, "Cargo.toml");
1316
+ const cargoPath = join4(cwd, "Cargo.toml");
994
1317
  if (await fileExists(cargoPath)) {
995
1318
  try {
996
- const content = await readFile(cargoPath, "utf-8");
1319
+ const content = await readFile3(cargoPath, "utf-8");
997
1320
  detected.stack.push("rust");
1321
+ detected.type = "application";
998
1322
  const nameMatch = content.match(/name\s*=\s*"([^"]+)"/);
999
1323
  if (nameMatch) detected.name = nameMatch[1];
1324
+ if (content.includes("actix-web")) detected.stack.push("actix");
1325
+ if (content.includes("axum")) detected.stack.push("axum");
1326
+ if (content.includes("tokio")) detected.stack.push("tokio");
1327
+ if (content.includes("serde")) detected.stack.push("serde");
1328
+ if (content.includes("sqlx")) detected.stack.push("sqlx");
1000
1329
  detected.commands.build = "cargo build";
1001
1330
  detected.commands.test = "cargo test";
1002
1331
  detected.commands.lint = "cargo clippy";
1332
+ detected.commands.dev = "cargo run";
1003
1333
  return detected;
1004
1334
  } catch {
1005
1335
  }
1006
1336
  }
1007
- const goModPath = join3(cwd, "go.mod");
1337
+ const goModPath = join4(cwd, "go.mod");
1008
1338
  if (await fileExists(goModPath)) {
1009
1339
  try {
1010
- const content = await readFile(goModPath, "utf-8");
1340
+ const content = await readFile3(goModPath, "utf-8");
1011
1341
  detected.stack.push("go");
1342
+ detected.type = "application";
1012
1343
  const moduleMatch = content.match(/module\s+(\S+)/);
1013
1344
  if (moduleMatch) {
1014
1345
  const parts = moduleMatch[1].split("/");
1015
1346
  detected.name = parts[parts.length - 1];
1016
1347
  }
1348
+ if (content.includes("gin-gonic/gin")) detected.stack.push("gin");
1349
+ if (content.includes("gofiber/fiber")) detected.stack.push("fiber");
1350
+ if (content.includes("labstack/echo")) detected.stack.push("echo");
1351
+ if (content.includes("gorm.io/gorm")) detected.stack.push("gorm");
1017
1352
  detected.commands.build = "go build";
1018
1353
  detected.commands.test = "go test ./...";
1019
1354
  detected.commands.lint = "golangci-lint run";
1355
+ detected.commands.dev = "go run .";
1020
1356
  return detected;
1021
1357
  } catch {
1022
1358
  }
1023
1359
  }
1024
- const makefilePath = join3(cwd, "Makefile");
1360
+ const makefilePath = join4(cwd, "Makefile");
1025
1361
  if (await fileExists(makefilePath)) {
1026
1362
  try {
1027
- const content = await readFile(makefilePath, "utf-8");
1363
+ const content = await readFile3(makefilePath, "utf-8");
1028
1364
  if (content.includes("build:")) detected.commands.build = "make build";
1029
1365
  if (content.includes("test:")) detected.commands.test = "make test";
1030
1366
  if (content.includes("lint:")) detected.commands.lint = "make lint";
1367
+ if (content.includes("dev:")) detected.commands.dev = "make dev";
1368
+ if (content.includes("run:")) detected.commands.dev = detected.commands.dev || "make run";
1031
1369
  if (Object.keys(detected.commands).length > 0) {
1370
+ detected.type = "application";
1032
1371
  return detected;
1033
1372
  }
1034
1373
  } catch {
1035
1374
  }
1036
1375
  }
1376
+ if (await fileExists(join4(cwd, "Dockerfile")) || await fileExists(join4(cwd, "docker-compose.yml"))) {
1377
+ detected.stack.push("docker");
1378
+ detected.type = "application";
1379
+ }
1037
1380
  return detected.stack.length > 0 || detected.name ? detected : null;
1038
1381
  }
1039
1382
  async function fileExists(path) {
1040
1383
  try {
1041
- await access2(path);
1384
+ await access3(path);
1042
1385
  return true;
1043
1386
  } catch {
1044
1387
  return false;
@@ -1049,13 +1392,28 @@ async function fileExists(path) {
1049
1392
  var LYNXPROMPT_DIR = ".lynxprompt";
1050
1393
  var LYNXPROMPT_CONFIG = ".lynxprompt/conf.yml";
1051
1394
  var LYNXPROMPT_RULES = ".lynxprompt/rules";
1395
+ var AGENT_FILES = [
1396
+ { name: "AGENTS.md", agent: "Universal (AGENTS.md)" },
1397
+ { name: "CLAUDE.md", agent: "Claude Code" },
1398
+ { name: ".windsurfrules", agent: "Windsurf" },
1399
+ { name: ".clinerules", agent: "Cline" },
1400
+ { name: ".goosehints", agent: "Goose" },
1401
+ { name: "AIDER.md", agent: "Aider" },
1402
+ { name: ".github/copilot-instructions.md", agent: "GitHub Copilot" },
1403
+ { name: ".zed/instructions.md", agent: "Zed" }
1404
+ ];
1405
+ var AGENT_DIRS = [
1406
+ { path: ".cursor/rules", agent: "Cursor" },
1407
+ { path: ".amazonq/rules", agent: "Amazon Q" },
1408
+ { path: ".augment/rules", agent: "Augment Code" }
1409
+ ];
1052
1410
  async function scanForExistingFiles(cwd) {
1053
1411
  const detected = [];
1054
1412
  for (const file of AGENT_FILES) {
1055
- const filePath = join4(cwd, file.name);
1056
- if (existsSync2(filePath)) {
1413
+ const filePath = join5(cwd, file.name);
1414
+ if (existsSync3(filePath)) {
1057
1415
  try {
1058
- const content = await readFile2(filePath, "utf-8");
1416
+ const content = await readFile4(filePath, "utf-8");
1059
1417
  detected.push({ path: file.name, agent: file.agent, content });
1060
1418
  } catch {
1061
1419
  detected.push({ path: file.name, agent: file.agent });
@@ -1063,8 +1421,8 @@ async function scanForExistingFiles(cwd) {
1063
1421
  }
1064
1422
  }
1065
1423
  for (const dir of AGENT_DIRS) {
1066
- const dirPath = join4(cwd, dir.path);
1067
- if (existsSync2(dirPath)) {
1424
+ const dirPath = join5(cwd, dir.path);
1425
+ if (existsSync3(dirPath)) {
1068
1426
  detected.push({ path: dir.path, agent: dir.agent });
1069
1427
  }
1070
1428
  }
@@ -1152,13 +1510,16 @@ function createDefaultConfig(exporters = ["agents"]) {
1152
1510
  }
1153
1511
  ]
1154
1512
  };
1155
- return yaml.stringify(config2);
1513
+ return yaml2.stringify(config2);
1156
1514
  }
1157
1515
  function createLynxpromptReadme() {
1158
1516
  return `# .lynxprompt
1159
1517
 
1160
1518
  This directory contains your LynxPrompt configuration and rules.
1161
1519
 
1520
+ > **Note**: This is an advanced setup for managing rules across multiple AI editors.
1521
+ > Most users should use \`lynxp wizard\` instead for simple, direct file generation.
1522
+
1162
1523
  ## Directory structure
1163
1524
 
1164
1525
  - **\`rules/\`** - Your AI rules. Edit files here, then sync to agents.
@@ -1182,7 +1543,7 @@ After editing rules, run:
1182
1543
  lynxp sync
1183
1544
  \`\`\`
1184
1545
 
1185
- This exports your rules to the configured agent formats (AGENTS.md, .cursorrules, etc.)
1546
+ This exports your rules to the configured agent formats (AGENTS.md, .cursor/rules/, etc.)
1186
1547
 
1187
1548
  ## More information
1188
1549
 
@@ -1193,13 +1554,31 @@ This exports your rules to the configured agent formats (AGENTS.md, .cursorrules
1193
1554
  async function initCommand(options) {
1194
1555
  console.log();
1195
1556
  console.log(chalk6.cyan("\u{1F431} LynxPrompt Init"));
1557
+ console.log(chalk6.gray("Advanced mode: Multi-editor rule management"));
1196
1558
  console.log();
1559
+ if (!options.yes && !options.force) {
1560
+ console.log(chalk6.yellow("\u{1F4A1} Tip: Most users should use 'lynxp wizard' instead."));
1561
+ console.log(chalk6.gray(" The wizard generates files directly without the .lynxprompt/ folder."));
1562
+ console.log();
1563
+ const { proceed } = await prompts2({
1564
+ type: "confirm",
1565
+ name: "proceed",
1566
+ message: "Continue with advanced setup?",
1567
+ initial: false
1568
+ });
1569
+ if (!proceed) {
1570
+ console.log();
1571
+ console.log(chalk6.gray("Run 'lynxp wizard' for simple file generation."));
1572
+ return;
1573
+ }
1574
+ console.log();
1575
+ }
1197
1576
  const cwd = process.cwd();
1198
1577
  const projectName = basename(cwd);
1199
- const lynxpromptDir = join4(cwd, LYNXPROMPT_DIR);
1200
- const configPath = join4(cwd, LYNXPROMPT_CONFIG);
1201
- const rulesDir = join4(cwd, LYNXPROMPT_RULES);
1202
- if (existsSync2(configPath) && !options.force) {
1578
+ const lynxpromptDir = join5(cwd, LYNXPROMPT_DIR);
1579
+ const configPath = join5(cwd, LYNXPROMPT_CONFIG);
1580
+ const rulesDir = join5(cwd, LYNXPROMPT_RULES);
1581
+ if (existsSync3(configPath) && !options.force) {
1203
1582
  console.log(chalk6.yellow("LynxPrompt is already initialized in this project."));
1204
1583
  console.log(chalk6.gray(`Config: ${LYNXPROMPT_CONFIG}`));
1205
1584
  console.log(chalk6.gray(`Rules: ${LYNXPROMPT_RULES}/`));
@@ -1216,23 +1595,22 @@ async function initCommand(options) {
1216
1595
  const existingFiles = await scanForExistingFiles(cwd);
1217
1596
  spinner.stop();
1218
1597
  if (projectInfo) {
1219
- console.log(chalk6.gray("Detected project:"));
1598
+ console.log(chalk6.green("\u2713 Detected project:"));
1220
1599
  if (projectInfo.name) console.log(chalk6.gray(` Name: ${projectInfo.name}`));
1221
1600
  if (projectInfo.stack.length > 0) console.log(chalk6.gray(` Stack: ${projectInfo.stack.join(", ")}`));
1222
1601
  if (projectInfo.packageManager) console.log(chalk6.gray(` Package manager: ${projectInfo.packageManager}`));
1223
1602
  console.log();
1224
1603
  }
1225
1604
  if (agentDetection.detected.length > 0) {
1226
- console.log(chalk6.green(`Detected ${agentDetection.detected.length} AI agent${agentDetection.detected.length === 1 ? "" : "s"}:`));
1605
+ console.log(chalk6.green(`\u2713 Detected ${agentDetection.detected.length} AI agent${agentDetection.detected.length === 1 ? "" : "s"}:`));
1227
1606
  for (const detected of agentDetection.detected) {
1228
1607
  const rules = detected.ruleCount > 0 ? chalk6.gray(` (${detected.ruleCount} sections)`) : "";
1229
- console.log(` ${chalk6.cyan("\u2713")} ${detected.agent.name}${rules}`);
1608
+ console.log(` ${chalk6.cyan("\u2022")} ${detected.agent.name}${rules}`);
1230
1609
  }
1231
1610
  console.log();
1232
1611
  }
1233
1612
  if (existingFiles.length > 0) {
1234
- console.log(chalk6.green("Found existing AI configuration files:"));
1235
- console.log();
1613
+ console.log(chalk6.green("\u2713 Found existing AI configuration files:"));
1236
1614
  for (const file of existingFiles) {
1237
1615
  console.log(` ${chalk6.cyan(file.path)} ${chalk6.gray(`(${file.agent})`)}`);
1238
1616
  }
@@ -1253,35 +1631,35 @@ async function initCommand(options) {
1253
1631
  return;
1254
1632
  }
1255
1633
  if (action === "import") {
1256
- await mkdir2(rulesDir, { recursive: true });
1634
+ await mkdir3(rulesDir, { recursive: true });
1257
1635
  let importedCount = 0;
1258
1636
  for (const file of existingFiles) {
1259
1637
  if (file.content) {
1260
1638
  const ruleName = file.path.replace(/^\./, "").replace(/\//g, "-").replace(/\.md$/, "") + ".md";
1261
- const rulePath = join4(rulesDir, ruleName);
1262
- await writeFile2(rulePath, file.content, "utf-8");
1639
+ const rulePath = join5(rulesDir, ruleName);
1640
+ await writeFile3(rulePath, file.content, "utf-8");
1263
1641
  console.log(chalk6.gray(` Imported: ${file.path} \u2192 .lynxprompt/rules/${ruleName}`));
1264
1642
  importedCount++;
1265
1643
  }
1266
1644
  }
1267
1645
  if (importedCount === 0) {
1268
- const starterPath = join4(rulesDir, "agents.md");
1269
- await writeFile2(starterPath, createStarterAgentsMd(projectName), "utf-8");
1646
+ const starterPath = join5(rulesDir, "agents.md");
1647
+ await writeFile3(starterPath, createStarterAgentsMd(projectName), "utf-8");
1270
1648
  console.log(chalk6.gray(" Created starter: .lynxprompt/rules/agents.md"));
1271
1649
  }
1272
1650
  } else {
1273
- await mkdir2(rulesDir, { recursive: true });
1274
- const starterPath = join4(rulesDir, "agents.md");
1275
- await writeFile2(starterPath, createStarterAgentsMd(projectName), "utf-8");
1651
+ await mkdir3(rulesDir, { recursive: true });
1652
+ const starterPath = join5(rulesDir, "agents.md");
1653
+ await writeFile3(starterPath, createStarterAgentsMd(projectName), "utf-8");
1276
1654
  console.log(chalk6.gray("Created starter: .lynxprompt/rules/agents.md"));
1277
1655
  }
1278
1656
  } else {
1279
- await mkdir2(rulesDir, { recursive: true });
1657
+ await mkdir3(rulesDir, { recursive: true });
1280
1658
  for (const file of existingFiles) {
1281
1659
  if (file.content) {
1282
1660
  const ruleName = file.path.replace(/^\./, "").replace(/\//g, "-").replace(/\.md$/, "") + ".md";
1283
- const rulePath = join4(rulesDir, ruleName);
1284
- await writeFile2(rulePath, file.content, "utf-8");
1661
+ const rulePath = join5(rulesDir, ruleName);
1662
+ await writeFile3(rulePath, file.content, "utf-8");
1285
1663
  }
1286
1664
  }
1287
1665
  }
@@ -1292,7 +1670,7 @@ async function initCommand(options) {
1292
1670
  const { create } = await prompts2({
1293
1671
  type: "confirm",
1294
1672
  name: "create",
1295
- message: "Create a starter AGENTS.md template?",
1673
+ message: "Create a starter template?",
1296
1674
  initial: true
1297
1675
  });
1298
1676
  if (!create) {
@@ -1300,9 +1678,9 @@ async function initCommand(options) {
1300
1678
  return;
1301
1679
  }
1302
1680
  }
1303
- await mkdir2(rulesDir, { recursive: true });
1304
- const starterPath = join4(rulesDir, "agents.md");
1305
- await writeFile2(starterPath, createStarterAgentsMd(projectName), "utf-8");
1681
+ await mkdir3(rulesDir, { recursive: true });
1682
+ const starterPath = join5(rulesDir, "agents.md");
1683
+ await writeFile3(starterPath, createStarterAgentsMd(projectName), "utf-8");
1306
1684
  console.log(chalk6.gray("Created: .lynxprompt/rules/agents.md"));
1307
1685
  }
1308
1686
  let exporters = [];
@@ -1346,16 +1724,16 @@ async function initCommand(options) {
1346
1724
  }
1347
1725
  }
1348
1726
  console.log(chalk6.gray(`Enabling ${exporters.length} exporter${exporters.length === 1 ? "" : "s"}: ${exporters.join(", ")}`));
1349
- await mkdir2(dirname2(configPath), { recursive: true });
1350
- await writeFile2(configPath, createDefaultConfig(exporters), "utf-8");
1351
- const readmePath = join4(lynxpromptDir, "README.md");
1352
- await writeFile2(readmePath, createLynxpromptReadme(), "utf-8");
1353
- const gitignorePath = join4(lynxpromptDir, ".gitignore");
1727
+ await mkdir3(dirname3(configPath), { recursive: true });
1728
+ await writeFile3(configPath, createDefaultConfig(exporters), "utf-8");
1729
+ const readmePath = join5(lynxpromptDir, "README.md");
1730
+ await writeFile3(readmePath, createLynxpromptReadme(), "utf-8");
1731
+ const gitignorePath = join5(lynxpromptDir, ".gitignore");
1354
1732
  const gitignoreContent = `# Local state files
1355
1733
  .cache/
1356
1734
  .backups/
1357
1735
  `;
1358
- await writeFile2(gitignorePath, gitignoreContent, "utf-8");
1736
+ await writeFile3(gitignorePath, gitignoreContent, "utf-8");
1359
1737
  console.log();
1360
1738
  console.log(chalk6.green("\u2705 LynxPrompt initialized!"));
1361
1739
  console.log();
@@ -1374,13 +1752,14 @@ async function initCommand(options) {
1374
1752
  import chalk7 from "chalk";
1375
1753
  import prompts3 from "prompts";
1376
1754
  import ora6 from "ora";
1377
- import { writeFile as writeFile3, mkdir as mkdir3, access as access4 } from "fs/promises";
1378
- import { join as join5, dirname as dirname3 } from "path";
1755
+ import { writeFile as writeFile4, mkdir as mkdir4, access as access5 } from "fs/promises";
1756
+ import { join as join6, dirname as dirname4 } from "path";
1379
1757
 
1380
1758
  // src/utils/generator.ts
1381
1759
  var PLATFORM_FILES = {
1382
- cursor: ".cursorrules",
1383
- claude: "AGENTS.md",
1760
+ agents: "AGENTS.md",
1761
+ cursor: ".cursor/rules/project.mdc",
1762
+ claude: "CLAUDE.md",
1384
1763
  copilot: ".github/copilot-instructions.md",
1385
1764
  windsurf: ".windsurfrules",
1386
1765
  zed: ".zed/instructions.md"
@@ -1490,13 +1869,25 @@ function generateConfig(options) {
1490
1869
  }
1491
1870
  function generateFileContent(options, platform) {
1492
1871
  const sections = [];
1493
- const isMarkdown = platform !== "cursor" && platform !== "windsurf";
1872
+ const isMdc = platform === "cursor";
1873
+ const isPlainText = platform === "windsurf";
1874
+ const isMarkdown = !isMdc && !isPlainText;
1875
+ if (isMdc) {
1876
+ sections.push("---");
1877
+ sections.push(`description: "${options.name} - AI coding rules"`);
1878
+ sections.push('globs: ["**/*"]');
1879
+ sections.push("alwaysApply: true");
1880
+ sections.push("---");
1881
+ sections.push("");
1882
+ sections.push(`# ${options.name} - AI Assistant Configuration`);
1883
+ sections.push("");
1884
+ }
1494
1885
  if (isMarkdown) {
1495
1886
  sections.push(`# ${options.name} - AI Assistant Configuration`);
1496
1887
  sections.push("");
1497
1888
  }
1498
1889
  const personaDesc = PERSONA_DESCRIPTIONS[options.persona] || options.persona;
1499
- if (isMarkdown) {
1890
+ if (isMarkdown || isMdc) {
1500
1891
  sections.push("## Persona");
1501
1892
  sections.push("");
1502
1893
  sections.push(`You are ${personaDesc}. You assist developers working on ${options.name}.`);
@@ -1509,14 +1900,14 @@ function generateFileContent(options, platform) {
1509
1900
  }
1510
1901
  sections.push("");
1511
1902
  if (options.stack.length > 0) {
1512
- if (isMarkdown) {
1903
+ if (isMarkdown || isMdc) {
1513
1904
  sections.push("## Tech Stack");
1514
1905
  sections.push("");
1515
1906
  } else {
1516
1907
  sections.push("Tech Stack:");
1517
1908
  }
1518
1909
  const stackList = options.stack.map((s) => STACK_NAMES[s] || s);
1519
- if (isMarkdown) {
1910
+ if (isMarkdown || isMdc) {
1520
1911
  for (const tech of stackList) {
1521
1912
  sections.push(`- ${tech}`);
1522
1913
  }
@@ -1527,7 +1918,7 @@ function generateFileContent(options, platform) {
1527
1918
  }
1528
1919
  const hasCommands = Object.values(options.commands).some(Boolean);
1529
1920
  if (hasCommands) {
1530
- if (isMarkdown) {
1921
+ if (isMarkdown || isMdc) {
1531
1922
  sections.push("## Commands");
1532
1923
  sections.push("");
1533
1924
  sections.push("Use these commands for common tasks:");
@@ -1537,25 +1928,25 @@ function generateFileContent(options, platform) {
1537
1928
  sections.push("Commands:");
1538
1929
  }
1539
1930
  if (options.commands.build) {
1540
- sections.push(isMarkdown ? `# Build: ${options.commands.build}` : `- Build: ${options.commands.build}`);
1931
+ sections.push(isMarkdown || isMdc ? `# Build: ${options.commands.build}` : `- Build: ${options.commands.build}`);
1541
1932
  }
1542
1933
  if (options.commands.test) {
1543
- sections.push(isMarkdown ? `# Test: ${options.commands.test}` : `- Test: ${options.commands.test}`);
1934
+ sections.push(isMarkdown || isMdc ? `# Test: ${options.commands.test}` : `- Test: ${options.commands.test}`);
1544
1935
  }
1545
1936
  if (options.commands.lint) {
1546
- sections.push(isMarkdown ? `# Lint: ${options.commands.lint}` : `- Lint: ${options.commands.lint}`);
1937
+ sections.push(isMarkdown || isMdc ? `# Lint: ${options.commands.lint}` : `- Lint: ${options.commands.lint}`);
1547
1938
  }
1548
1939
  if (options.commands.dev) {
1549
- sections.push(isMarkdown ? `# Dev: ${options.commands.dev}` : `- Dev: ${options.commands.dev}`);
1940
+ sections.push(isMarkdown || isMdc ? `# Dev: ${options.commands.dev}` : `- Dev: ${options.commands.dev}`);
1550
1941
  }
1551
- if (isMarkdown) {
1942
+ if (isMarkdown || isMdc) {
1552
1943
  sections.push("```");
1553
1944
  }
1554
1945
  sections.push("");
1555
1946
  }
1556
1947
  const boundaries = BOUNDARIES[options.boundaries];
1557
1948
  if (boundaries) {
1558
- if (isMarkdown) {
1949
+ if (isMarkdown || isMdc) {
1559
1950
  sections.push("## Boundaries");
1560
1951
  sections.push("");
1561
1952
  sections.push("### \u2705 Always (do without asking)");
@@ -1595,7 +1986,7 @@ function generateFileContent(options, platform) {
1595
1986
  }
1596
1987
  sections.push("");
1597
1988
  }
1598
- if (isMarkdown) {
1989
+ if (isMarkdown || isMdc) {
1599
1990
  sections.push("## Code Style");
1600
1991
  sections.push("");
1601
1992
  sections.push("Follow these conventions:");
@@ -1631,7 +2022,7 @@ function generateFileContent(options, platform) {
1631
2022
  sections.push("- Keep functions focused and testable");
1632
2023
  sections.push("");
1633
2024
  }
1634
- if (isMarkdown) {
2025
+ if (isMarkdown || isMdc) {
1635
2026
  sections.push("---");
1636
2027
  sections.push("");
1637
2028
  sections.push(`*Generated by [LynxPrompt](https://lynxprompt.com) CLI*`);
@@ -1640,6 +2031,24 @@ function generateFileContent(options, platform) {
1640
2031
  }
1641
2032
 
1642
2033
  // src/commands/wizard.ts
2034
+ var OUTPUT_FORMATS = [
2035
+ {
2036
+ title: "AGENTS.md (Universal)",
2037
+ value: "agents",
2038
+ description: "Works with Claude Code, GitHub Copilot, Aider, and most AI editors",
2039
+ recommended: true
2040
+ },
2041
+ {
2042
+ title: "Cursor (.cursor/rules/)",
2043
+ value: "cursor",
2044
+ description: "Cursor IDE with MDC format"
2045
+ },
2046
+ {
2047
+ title: "Multiple formats",
2048
+ value: "multiple",
2049
+ description: "Select multiple AI editors to generate for"
2050
+ }
2051
+ ];
1643
2052
  var TECH_STACKS = [
1644
2053
  { title: "TypeScript", value: "typescript" },
1645
2054
  { title: "JavaScript", value: "javascript" },
@@ -1667,22 +2076,30 @@ var FRAMEWORKS = [
1667
2076
  { title: "Laravel", value: "laravel" }
1668
2077
  ];
1669
2078
  var PLATFORMS = [
1670
- { title: "Cursor (.cursorrules)", value: "cursor", filename: ".cursorrules" },
1671
- { title: "Claude Code (AGENTS.md)", value: "claude", filename: "AGENTS.md" },
2079
+ { title: "AGENTS.md (Universal)", value: "agents", filename: "AGENTS.md" },
2080
+ { title: "Cursor (.cursor/rules/)", value: "cursor", filename: ".cursor/rules/project.mdc" },
2081
+ { title: "Claude Code (CLAUDE.md)", value: "claude", filename: "CLAUDE.md" },
1672
2082
  { title: "GitHub Copilot", value: "copilot", filename: ".github/copilot-instructions.md" },
1673
2083
  { title: "Windsurf (.windsurfrules)", value: "windsurf", filename: ".windsurfrules" },
1674
2084
  { title: "Zed", value: "zed", filename: ".zed/instructions.md" }
1675
2085
  ];
1676
2086
  var PERSONAS = [
2087
+ { title: "Full-Stack Developer - Complete application setups", value: "fullstack" },
1677
2088
  { title: "Backend Developer - APIs, databases, microservices", value: "backend" },
1678
2089
  { title: "Frontend Developer - UI, components, styling", value: "frontend" },
1679
- { title: "Full-Stack Developer - Complete application setups", value: "fullstack" },
1680
2090
  { title: "DevOps Engineer - Infrastructure, CI/CD, containers", value: "devops" },
1681
2091
  { title: "Data Engineer - Pipelines, ETL, databases", value: "data" },
1682
2092
  { title: "Security Engineer - Secure code, vulnerabilities", value: "security" },
1683
2093
  { title: "Custom...", value: "custom" }
1684
2094
  ];
1685
2095
  var BOUNDARY_PRESETS = [
2096
+ {
2097
+ title: "Standard - Balance of freedom and safety (recommended)",
2098
+ value: "standard",
2099
+ always: ["Read any file", "Modify files in src/", "Run build/test/lint", "Create test files"],
2100
+ askFirst: ["Add new dependencies", "Modify config files", "Create new modules"],
2101
+ never: ["Delete production data", "Modify .env secrets", "Force push"]
2102
+ },
1686
2103
  {
1687
2104
  title: "Conservative - Ask before most changes",
1688
2105
  value: "conservative",
@@ -1690,13 +2107,6 @@ var BOUNDARY_PRESETS = [
1690
2107
  askFirst: ["Modify any file", "Add dependencies", "Create files", "Run tests"],
1691
2108
  never: ["Delete files", "Modify .env", "Push to git"]
1692
2109
  },
1693
- {
1694
- title: "Standard - Balance of freedom and safety",
1695
- value: "standard",
1696
- always: ["Read any file", "Modify files in src/", "Run build/test/lint", "Create test files"],
1697
- askFirst: ["Add new dependencies", "Modify config files", "Create new modules"],
1698
- never: ["Delete production data", "Modify .env secrets", "Force push"]
1699
- },
1700
2110
  {
1701
2111
  title: "Permissive - AI can modify freely within src/",
1702
2112
  value: "permissive",
@@ -1707,24 +2117,34 @@ var BOUNDARY_PRESETS = [
1707
2117
  ];
1708
2118
  async function wizardCommand(options) {
1709
2119
  console.log();
1710
- console.log(chalk7.cyan("\u{1F431} Welcome to LynxPrompt Wizard!"));
2120
+ console.log(chalk7.cyan("\u{1F431} LynxPrompt Wizard"));
2121
+ console.log(chalk7.gray("Generate AI IDE configuration in seconds"));
1711
2122
  console.log();
1712
2123
  const detected = await detectProject(process.cwd());
1713
2124
  if (detected) {
1714
- console.log(chalk7.gray("Detected project configuration:"));
2125
+ console.log(chalk7.green("\u2713 Detected project:"));
1715
2126
  if (detected.name) console.log(chalk7.gray(` Name: ${detected.name}`));
1716
2127
  if (detected.stack.length > 0) console.log(chalk7.gray(` Stack: ${detected.stack.join(", ")}`));
2128
+ if (detected.packageManager) console.log(chalk7.gray(` Package manager: ${detected.packageManager}`));
1717
2129
  if (detected.commands.build) console.log(chalk7.gray(` Build: ${detected.commands.build}`));
1718
2130
  if (detected.commands.test) console.log(chalk7.gray(` Test: ${detected.commands.test}`));
1719
2131
  console.log();
1720
2132
  }
1721
2133
  let config2;
1722
2134
  if (options.yes) {
2135
+ let platforms;
2136
+ if (options.format) {
2137
+ platforms = options.format.split(",").map((f) => f.trim());
2138
+ } else if (options.platforms) {
2139
+ platforms = options.platforms.split(",").map((p) => p.trim());
2140
+ } else {
2141
+ platforms = ["agents"];
2142
+ }
1723
2143
  config2 = {
1724
2144
  name: options.name || detected?.name || "my-project",
1725
2145
  description: options.description || "",
1726
2146
  stack: options.stack?.split(",").map((s) => s.trim()) || detected?.stack || [],
1727
- platforms: options.platforms?.split(",").map((s) => s.trim()) || ["cursor", "claude"],
2147
+ platforms,
1728
2148
  persona: options.persona || "fullstack",
1729
2149
  boundaries: options.boundaries || "standard",
1730
2150
  commands: detected?.commands || {}
@@ -1732,17 +2152,17 @@ async function wizardCommand(options) {
1732
2152
  } else {
1733
2153
  config2 = await runInteractiveWizard(options, detected);
1734
2154
  }
1735
- const spinner = ora6("Generating configuration files...").start();
2155
+ const spinner = ora6("Generating configuration...").start();
1736
2156
  try {
1737
2157
  const files = generateConfig(config2);
1738
2158
  spinner.stop();
1739
2159
  console.log();
1740
- console.log(chalk7.green("\u2705 Generated files:"));
2160
+ console.log(chalk7.green("\u2705 Generated:"));
1741
2161
  for (const [filename, content] of Object.entries(files)) {
1742
- const outputPath = join5(process.cwd(), filename);
2162
+ const outputPath = join6(process.cwd(), filename);
1743
2163
  let exists = false;
1744
2164
  try {
1745
- await access4(outputPath);
2165
+ await access5(outputPath);
1746
2166
  exists = true;
1747
2167
  } catch {
1748
2168
  }
@@ -1758,66 +2178,98 @@ async function wizardCommand(options) {
1758
2178
  continue;
1759
2179
  }
1760
2180
  }
1761
- const dir = dirname3(outputPath);
2181
+ const dir = dirname4(outputPath);
1762
2182
  if (dir !== ".") {
1763
- await mkdir3(dir, { recursive: true });
2183
+ await mkdir4(dir, { recursive: true });
1764
2184
  }
1765
- await writeFile3(outputPath, content, "utf-8");
2185
+ await writeFile4(outputPath, content, "utf-8");
1766
2186
  console.log(` ${chalk7.cyan(filename)}`);
1767
2187
  }
1768
2188
  console.log();
1769
- console.log(chalk7.gray("Your AI IDE configuration is ready!"));
1770
- console.log(chalk7.gray("The AI assistant in your IDE will now follow these instructions."));
2189
+ console.log(chalk7.gray("Your AI assistant will now follow these instructions."));
2190
+ console.log();
2191
+ console.log(chalk7.gray("Tips:"));
2192
+ console.log(chalk7.gray(" \u2022 Edit the generated file anytime to customize rules"));
2193
+ console.log(chalk7.gray(" \u2022 Run 'lynxp wizard' again to regenerate"));
2194
+ console.log(chalk7.gray(" \u2022 Run 'lynxp check' to validate your configuration"));
1771
2195
  console.log();
1772
2196
  } catch (error) {
1773
2197
  spinner.fail("Failed to generate files");
1774
- console.error(chalk7.red("An error occurred while generating configuration files."));
2198
+ console.error(chalk7.red("\n\u2717 An error occurred while generating configuration files."));
1775
2199
  if (error instanceof Error) {
1776
- console.error(chalk7.gray(error.message));
2200
+ console.error(chalk7.gray(` ${error.message}`));
1777
2201
  }
2202
+ console.error(chalk7.gray("\nTry running with --yes flag for default settings."));
1778
2203
  process.exit(1);
1779
2204
  }
1780
2205
  }
1781
2206
  async function runInteractiveWizard(options, detected) {
1782
2207
  const answers = {};
2208
+ let platforms;
2209
+ if (options.format) {
2210
+ platforms = options.format.split(",").map((f) => f.trim());
2211
+ } else {
2212
+ const formatResponse = await prompts3({
2213
+ type: "select",
2214
+ name: "format",
2215
+ message: "Select output format:",
2216
+ choices: OUTPUT_FORMATS.map((f) => ({
2217
+ title: f.recommended ? `${f.title} ${chalk7.green("(recommended)")}` : f.title,
2218
+ value: f.value,
2219
+ description: f.description
2220
+ })),
2221
+ initial: 0
2222
+ // AGENTS.md is default
2223
+ });
2224
+ if (formatResponse.format === "multiple") {
2225
+ const platformResponse = await prompts3({
2226
+ type: "multiselect",
2227
+ name: "platforms",
2228
+ message: "Select AI editors:",
2229
+ choices: PLATFORMS.map((p) => ({ title: p.title, value: p.value })),
2230
+ hint: "- Space to select, Enter to confirm",
2231
+ min: 1
2232
+ });
2233
+ platforms = platformResponse.platforms || ["agents"];
2234
+ } else {
2235
+ platforms = [formatResponse.format || "agents"];
2236
+ }
2237
+ }
2238
+ answers.platforms = platforms;
1783
2239
  const nameResponse = await prompts3({
1784
2240
  type: "text",
1785
2241
  name: "name",
1786
- message: "What's your project name?",
2242
+ message: "Project name:",
1787
2243
  initial: options.name || detected?.name || "my-project"
1788
2244
  });
1789
- answers.name = nameResponse.name;
2245
+ answers.name = nameResponse.name || "my-project";
1790
2246
  const descResponse = await prompts3({
1791
2247
  type: "text",
1792
2248
  name: "description",
1793
- message: "Describe your project in one sentence:",
2249
+ message: "Brief description (optional):",
1794
2250
  initial: options.description || ""
1795
2251
  });
1796
- answers.description = descResponse.description;
2252
+ answers.description = descResponse.description || "";
1797
2253
  const allStackOptions = [...TECH_STACKS, ...FRAMEWORKS];
2254
+ const detectedStackSet = new Set(detected?.stack || []);
1798
2255
  const stackResponse = await prompts3({
1799
2256
  type: "multiselect",
1800
2257
  name: "stack",
1801
- message: "Select your tech stack:",
1802
- choices: allStackOptions,
2258
+ message: "Tech stack:",
2259
+ choices: allStackOptions.map((s) => ({
2260
+ title: s.title,
2261
+ value: s.value,
2262
+ selected: detectedStackSet.has(s.value)
2263
+ })),
1803
2264
  hint: "- Space to select, Enter to confirm"
1804
2265
  });
1805
2266
  answers.stack = stackResponse.stack || [];
1806
- const platformResponse = await prompts3({
1807
- type: "multiselect",
1808
- name: "platforms",
1809
- message: "Which AI IDEs do you use?",
1810
- choices: PLATFORMS,
1811
- hint: "- Space to select, Enter to confirm",
1812
- min: 1
1813
- });
1814
- answers.platforms = platformResponse.platforms || ["cursor"];
1815
2267
  const personaResponse = await prompts3({
1816
2268
  type: "select",
1817
2269
  name: "persona",
1818
- message: "What's the AI's persona/role?",
2270
+ message: "AI persona:",
1819
2271
  choices: PERSONAS,
1820
- initial: 2
2272
+ initial: 0
1821
2273
  // Full-stack by default
1822
2274
  });
1823
2275
  if (personaResponse.persona === "custom") {
@@ -1828,8 +2280,17 @@ async function runInteractiveWizard(options, detected) {
1828
2280
  });
1829
2281
  answers.persona = customPersona.value || "fullstack";
1830
2282
  } else {
1831
- answers.persona = personaResponse.persona;
2283
+ answers.persona = personaResponse.persona || "fullstack";
1832
2284
  }
2285
+ const boundaryResponse = await prompts3({
2286
+ type: "select",
2287
+ name: "boundaries",
2288
+ message: "AI boundaries:",
2289
+ choices: BOUNDARY_PRESETS.map((b) => ({ title: b.title, value: b.value })),
2290
+ initial: 0
2291
+ // Standard by default
2292
+ });
2293
+ answers.boundaries = boundaryResponse.boundaries || "standard";
1833
2294
  if (detected?.commands && Object.keys(detected.commands).length > 0) {
1834
2295
  console.log();
1835
2296
  console.log(chalk7.gray("Auto-detected commands:"));
@@ -1840,15 +2301,15 @@ async function runInteractiveWizard(options, detected) {
1840
2301
  const editCommands = await prompts3({
1841
2302
  type: "confirm",
1842
2303
  name: "edit",
1843
- message: "Edit these commands?",
2304
+ message: "Edit commands?",
1844
2305
  initial: false
1845
2306
  });
1846
2307
  if (editCommands.edit) {
1847
2308
  const commandsResponse = await prompts3([
1848
- { type: "text", name: "build", message: "Build command:", initial: detected.commands.build },
1849
- { type: "text", name: "test", message: "Test command:", initial: detected.commands.test },
1850
- { type: "text", name: "lint", message: "Lint command:", initial: detected.commands.lint },
1851
- { type: "text", name: "dev", message: "Dev command:", initial: detected.commands.dev }
2309
+ { type: "text", name: "build", message: "Build:", initial: detected.commands.build },
2310
+ { type: "text", name: "test", message: "Test:", initial: detected.commands.test },
2311
+ { type: "text", name: "lint", message: "Lint:", initial: detected.commands.lint },
2312
+ { type: "text", name: "dev", message: "Dev:", initial: detected.commands.dev }
1852
2313
  ]);
1853
2314
  answers.commands = commandsResponse;
1854
2315
  } else {
@@ -1857,15 +2318,6 @@ async function runInteractiveWizard(options, detected) {
1857
2318
  } else {
1858
2319
  answers.commands = {};
1859
2320
  }
1860
- const boundaryResponse = await prompts3({
1861
- type: "select",
1862
- name: "boundaries",
1863
- message: "Select boundary preset:",
1864
- choices: BOUNDARY_PRESETS.map((b) => ({ title: b.title, value: b.value })),
1865
- initial: 1
1866
- // Standard by default
1867
- });
1868
- answers.boundaries = boundaryResponse.boundaries || "standard";
1869
2321
  return {
1870
2322
  name: answers.name,
1871
2323
  description: answers.description,
@@ -1940,32 +2392,87 @@ function handleApiError3(error) {
1940
2392
 
1941
2393
  // src/commands/status.ts
1942
2394
  import chalk9 from "chalk";
1943
- import { access as access5, readFile as readFile4 } from "fs/promises";
1944
- import { join as join6 } from "path";
2395
+ import { access as access6, readFile as readFile6, readdir as readdir3 } from "fs/promises";
2396
+ import { join as join7 } from "path";
2397
+ import { existsSync as existsSync4 } from "fs";
1945
2398
  var CONFIG_FILES = [
1946
2399
  { path: "AGENTS.md", name: "AGENTS.md", platform: "Claude Code, Cursor, AI Agents" },
1947
2400
  { path: "CLAUDE.md", name: "CLAUDE.md", platform: "Claude Code" },
1948
- { path: ".cursorrules", name: ".cursorrules", platform: "Cursor" },
1949
2401
  { path: ".github/copilot-instructions.md", name: "Copilot Instructions", platform: "GitHub Copilot" },
1950
2402
  { path: ".windsurfrules", name: ".windsurfrules", platform: "Windsurf" },
1951
- { path: ".zed/instructions.md", name: "Zed Instructions", platform: "Zed" }
2403
+ { path: ".zed/instructions.md", name: "Zed Instructions", platform: "Zed" },
2404
+ { path: ".clinerules", name: ".clinerules", platform: "Cline" },
2405
+ { path: ".goosehints", name: ".goosehints", platform: "Goose" },
2406
+ { path: "AIDER.md", name: "AIDER.md", platform: "Aider" }
2407
+ ];
2408
+ var CONFIG_DIRS = [
2409
+ { path: ".cursor/rules", name: ".cursor/rules/", platform: "Cursor" },
2410
+ { path: ".amazonq/rules", name: ".amazonq/rules/", platform: "Amazon Q" },
2411
+ { path: ".augment/rules", name: ".augment/rules/", platform: "Augment Code" }
1952
2412
  ];
1953
2413
  async function statusCommand() {
1954
2414
  const cwd = process.cwd();
1955
2415
  console.log();
1956
- console.log(chalk9.cyan("\u{1F431} AI Config Status"));
2416
+ console.log(chalk9.cyan("\u{1F431} LynxPrompt Status"));
1957
2417
  console.log(chalk9.gray(` Directory: ${cwd}`));
1958
2418
  console.log();
2419
+ const lynxpromptExists = existsSync4(join7(cwd, ".lynxprompt"));
2420
+ if (lynxpromptExists) {
2421
+ console.log(chalk9.green("\u2713 LynxPrompt initialized"));
2422
+ const configPath = join7(cwd, ".lynxprompt/conf.yml");
2423
+ if (existsSync4(configPath)) {
2424
+ try {
2425
+ const content = await readFile6(configPath, "utf-8");
2426
+ const { parse: parse5 } = await import("yaml");
2427
+ const config2 = parse5(content);
2428
+ if (config2?.exporters?.length > 0) {
2429
+ console.log(chalk9.gray(` Exporters: ${config2.exporters.join(", ")}`));
2430
+ }
2431
+ } catch {
2432
+ }
2433
+ }
2434
+ console.log();
2435
+ }
2436
+ const trackedStatus = await checkSyncStatus(cwd);
2437
+ if (trackedStatus.length > 0) {
2438
+ console.log(chalk9.cyan("\u{1F4E6} Tracked Blueprints"));
2439
+ console.log();
2440
+ for (const { blueprint, localModified, fileExists: fileExists2 } of trackedStatus) {
2441
+ const statusIcon = !fileExists2 ? chalk9.red("\u2717") : localModified ? chalk9.yellow("\u25CF") : chalk9.green("\u2713");
2442
+ const sourceLabel = {
2443
+ marketplace: chalk9.gray("[marketplace]"),
2444
+ team: chalk9.blue("[team]"),
2445
+ private: chalk9.green("[private]"),
2446
+ local: chalk9.gray("[local]")
2447
+ }[blueprint.source];
2448
+ console.log(` ${statusIcon} ${chalk9.bold(blueprint.file)} ${sourceLabel}`);
2449
+ console.log(` ${chalk9.gray(`ID: ${blueprint.id} \u2022 ${blueprint.name}`)}`);
2450
+ if (!fileExists2) {
2451
+ console.log(chalk9.red(` \u26A0 File missing - run 'lynxp pull ${blueprint.id}' to restore`));
2452
+ } else if (localModified) {
2453
+ if (blueprint.source === "marketplace") {
2454
+ console.log(chalk9.yellow(` \u26A0 Local changes (marketplace = read-only, won't sync back)`));
2455
+ } else {
2456
+ console.log(chalk9.yellow(` \u26A0 Local changes - run 'lynxp push ${blueprint.file}' to sync`));
2457
+ }
2458
+ }
2459
+ console.log();
2460
+ }
2461
+ }
2462
+ console.log(chalk9.cyan("\u{1F4C4} AI Config Files"));
2463
+ console.log();
1959
2464
  let foundAny = false;
1960
2465
  for (const config2 of CONFIG_FILES) {
1961
- const filePath = join6(cwd, config2.path);
2466
+ const filePath = join7(cwd, config2.path);
1962
2467
  try {
1963
- await access5(filePath);
1964
- const content = await readFile4(filePath, "utf-8");
2468
+ await access6(filePath);
2469
+ const content = await readFile6(filePath, "utf-8");
1965
2470
  const lines = content.split("\n").length;
1966
2471
  const size = formatBytes(content.length);
1967
2472
  foundAny = true;
1968
- console.log(` ${chalk9.green("\u2713")} ${chalk9.bold(config2.name)}`);
2473
+ const tracked = trackedStatus.find((t) => t.blueprint.file === config2.path);
2474
+ const trackedLabel = tracked ? chalk9.cyan(" (tracked)") : "";
2475
+ console.log(` ${chalk9.green("\u2713")} ${chalk9.bold(config2.name)}${trackedLabel}`);
1969
2476
  console.log(` ${chalk9.gray(`Platform: ${config2.platform}`)}`);
1970
2477
  console.log(` ${chalk9.gray(`Size: ${size} (${lines} lines)`)}`);
1971
2478
  const preview = getPreview(content);
@@ -1976,13 +2483,41 @@ async function statusCommand() {
1976
2483
  } catch {
1977
2484
  }
1978
2485
  }
2486
+ for (const config2 of CONFIG_DIRS) {
2487
+ const dirPath = join7(cwd, config2.path);
2488
+ if (existsSync4(dirPath)) {
2489
+ try {
2490
+ const files = await readdir3(dirPath);
2491
+ const ruleFiles = files.filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
2492
+ if (ruleFiles.length > 0) {
2493
+ foundAny = true;
2494
+ console.log(` ${chalk9.green("\u2713")} ${chalk9.bold(config2.name)}`);
2495
+ console.log(` ${chalk9.gray(`Platform: ${config2.platform}`)}`);
2496
+ console.log(` ${chalk9.gray(`Rules: ${ruleFiles.length} file${ruleFiles.length === 1 ? "" : "s"}`)}`);
2497
+ for (const file of ruleFiles.slice(0, 3)) {
2498
+ console.log(` ${chalk9.gray(` \u2022 ${file}`)}`);
2499
+ }
2500
+ if (ruleFiles.length > 3) {
2501
+ console.log(` ${chalk9.gray(` ... and ${ruleFiles.length - 3} more`)}`);
2502
+ }
2503
+ console.log();
2504
+ }
2505
+ } catch {
2506
+ }
2507
+ }
2508
+ }
1979
2509
  if (!foundAny) {
1980
- console.log(chalk9.yellow(" No AI configuration files found in this directory."));
2510
+ console.log(chalk9.yellow(" No AI configuration files found."));
1981
2511
  console.log();
1982
- console.log(chalk9.gray(" Run 'lynxprompt init' to create a configuration."));
1983
- console.log(chalk9.gray(" Or run 'lynxprompt pull <id>' to download an existing blueprint."));
2512
+ console.log(chalk9.gray(" Get started:"));
2513
+ console.log(chalk9.gray(" lynxp wizard Generate a configuration"));
2514
+ console.log(chalk9.gray(" lynxp pull <id> Download from marketplace"));
2515
+ console.log(chalk9.gray(" lynxp search <query> Search for blueprints"));
1984
2516
  } else {
1985
- console.log(chalk9.gray("Run 'lynxprompt init' to update or create additional configs."));
2517
+ console.log(chalk9.gray("Commands:"));
2518
+ console.log(chalk9.gray(" lynxp wizard Regenerate configuration"));
2519
+ console.log(chalk9.gray(" lynxp check Validate files"));
2520
+ console.log(chalk9.gray(" lynxp link --list Show tracked blueprints"));
1986
2521
  }
1987
2522
  console.log();
1988
2523
  }
@@ -1990,7 +2525,7 @@ function getPreview(content) {
1990
2525
  const lines = content.split("\n");
1991
2526
  for (const line of lines) {
1992
2527
  const trimmed = line.trim();
1993
- if (trimmed && !trimmed.startsWith("#") && !trimmed.startsWith("//") && !trimmed.startsWith("<!--")) {
2528
+ if (trimmed && !trimmed.startsWith("#") && !trimmed.startsWith("//") && !trimmed.startsWith("<!--") && !trimmed.startsWith("---") && !trimmed.startsWith(">")) {
1994
2529
  return truncate3(trimmed, 50);
1995
2530
  }
1996
2531
  }
@@ -2010,10 +2545,10 @@ function formatBytes(bytes) {
2010
2545
  import chalk10 from "chalk";
2011
2546
  import ora8 from "ora";
2012
2547
  import prompts4 from "prompts";
2013
- import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, readdir as readdir2 } from "fs/promises";
2014
- import { join as join7, dirname as dirname4 } from "path";
2015
- import { existsSync as existsSync3 } from "fs";
2016
- import * as yaml2 from "yaml";
2548
+ import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5, readdir as readdir4 } from "fs/promises";
2549
+ import { join as join8, dirname as dirname5 } from "path";
2550
+ import { existsSync as existsSync5 } from "fs";
2551
+ import * as yaml3 from "yaml";
2017
2552
  var CONFIG_FILE = ".lynxprompt/conf.yml";
2018
2553
  var RULES_DIR = ".lynxprompt/rules";
2019
2554
  async function syncCommand(options = {}) {
@@ -2021,8 +2556,8 @@ async function syncCommand(options = {}) {
2021
2556
  console.log(chalk10.cyan("\u{1F431} LynxPrompt Sync"));
2022
2557
  console.log();
2023
2558
  const cwd = process.cwd();
2024
- const configPath = join7(cwd, CONFIG_FILE);
2025
- if (!existsSync3(configPath)) {
2559
+ const configPath = join8(cwd, CONFIG_FILE);
2560
+ if (!existsSync5(configPath)) {
2026
2561
  console.log(chalk10.yellow("LynxPrompt is not initialized in this project."));
2027
2562
  console.log();
2028
2563
  console.log(chalk10.gray("Run 'lynxp init' first to set up LynxPrompt."));
@@ -2031,8 +2566,8 @@ async function syncCommand(options = {}) {
2031
2566
  const spinner = ora8("Loading configuration...").start();
2032
2567
  let config2;
2033
2568
  try {
2034
- const configContent = await readFile5(configPath, "utf-8");
2035
- config2 = yaml2.parse(configContent);
2569
+ const configContent = await readFile7(configPath, "utf-8");
2570
+ config2 = yaml3.parse(configContent);
2036
2571
  spinner.succeed("Configuration loaded");
2037
2572
  } catch (error) {
2038
2573
  spinner.fail("Failed to load configuration");
@@ -2064,8 +2599,8 @@ async function syncCommand(options = {}) {
2064
2599
  }
2065
2600
  console.log(chalk10.gray(`Exporters: ${validExporters.map((e) => e.name).join(", ")}`));
2066
2601
  console.log();
2067
- const rulesPath = join7(cwd, RULES_DIR);
2068
- if (!existsSync3(rulesPath)) {
2602
+ const rulesPath = join8(cwd, RULES_DIR);
2603
+ if (!existsSync5(rulesPath)) {
2069
2604
  console.log(chalk10.yellow("No rules found."));
2070
2605
  console.log(chalk10.gray(`Create rules in ${RULES_DIR}/ to sync them.`));
2071
2606
  return;
@@ -2128,12 +2663,12 @@ async function syncCommand(options = {}) {
2128
2663
  async function loadRules(rulesPath) {
2129
2664
  const files = [];
2130
2665
  try {
2131
- const entries = await readdir2(rulesPath, { withFileTypes: true });
2666
+ const entries = await readdir4(rulesPath, { withFileTypes: true });
2132
2667
  for (const entry of entries) {
2133
2668
  if (!entry.isFile()) continue;
2134
2669
  if (!entry.name.endsWith(".md")) continue;
2135
- const filePath = join7(rulesPath, entry.name);
2136
- const content = await readFile5(filePath, "utf-8");
2670
+ const filePath = join8(rulesPath, entry.name);
2671
+ const content = await readFile7(filePath, "utf-8");
2137
2672
  if (content.trim()) {
2138
2673
  files.push({ name: entry.name, content: content.trim() });
2139
2674
  }
@@ -2148,26 +2683,26 @@ async function loadRules(rulesPath) {
2148
2683
  return { combined, files, fileCount: files.length };
2149
2684
  }
2150
2685
  async function syncToAgent(cwd, agent, content) {
2151
- const outputPath = join7(cwd, agent.output);
2686
+ const outputPath = join8(cwd, agent.output);
2152
2687
  if (agent.output.endsWith("/")) {
2153
2688
  await syncToDirectory(cwd, agent, content);
2154
2689
  return;
2155
2690
  }
2156
2691
  const formatted = formatForAgent(agent, content);
2157
- const dir = dirname4(outputPath);
2692
+ const dir = dirname5(outputPath);
2158
2693
  if (dir !== ".") {
2159
- await mkdir4(dir, { recursive: true });
2694
+ await mkdir5(dir, { recursive: true });
2160
2695
  }
2161
- await writeFile4(outputPath, formatted, "utf-8");
2696
+ await writeFile5(outputPath, formatted, "utf-8");
2162
2697
  }
2163
2698
  async function syncToDirectory(cwd, agent, content) {
2164
- const outputDir = join7(cwd, agent.output);
2165
- await mkdir4(outputDir, { recursive: true });
2699
+ const outputDir = join8(cwd, agent.output);
2700
+ await mkdir5(outputDir, { recursive: true });
2166
2701
  const extension = agent.format === "mdc" ? ".mdc" : ".md";
2167
2702
  const filename = `lynxprompt-rules${extension}`;
2168
- const outputPath = join7(outputDir, filename);
2703
+ const outputPath = join8(outputDir, filename);
2169
2704
  const formatted = formatForAgent(agent, content);
2170
- await writeFile4(outputPath, formatted, "utf-8");
2705
+ await writeFile5(outputPath, formatted, "utf-8");
2171
2706
  }
2172
2707
  function formatForAgent(agent, content) {
2173
2708
  switch (agent.format) {
@@ -2184,7 +2719,7 @@ function formatForAgent(agent, content) {
2184
2719
  }
2185
2720
  }
2186
2721
  function formatAsMdc(content, agent) {
2187
- const frontmatter = yaml2.stringify({
2722
+ const frontmatter = yaml3.stringify({
2188
2723
  description: "LynxPrompt rules - AI coding guidelines",
2189
2724
  globs: ["**/*"],
2190
2725
  alwaysApply: true
@@ -2221,10 +2756,10 @@ function formatAsJson(content, agent) {
2221
2756
  // src/commands/agents.ts
2222
2757
  import chalk11 from "chalk";
2223
2758
  import prompts5 from "prompts";
2224
- import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
2225
- import { join as join8 } from "path";
2226
- import { existsSync as existsSync4 } from "fs";
2227
- import * as yaml3 from "yaml";
2759
+ import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
2760
+ import { join as join9 } from "path";
2761
+ import { existsSync as existsSync6 } from "fs";
2762
+ import * as yaml4 from "yaml";
2228
2763
  var CONFIG_FILE2 = ".lynxprompt/conf.yml";
2229
2764
  async function agentsCommand(action, agentId, options = {}) {
2230
2765
  console.log();
@@ -2291,8 +2826,8 @@ async function listAgents() {
2291
2826
  }
2292
2827
  async function enableAgent(agentId, options = {}) {
2293
2828
  const cwd = process.cwd();
2294
- const configPath = join8(cwd, CONFIG_FILE2);
2295
- if (!existsSync4(configPath)) {
2829
+ const configPath = join9(cwd, CONFIG_FILE2);
2830
+ if (!existsSync6(configPath)) {
2296
2831
  console.log(chalk11.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
2297
2832
  return;
2298
2833
  }
@@ -2361,8 +2896,8 @@ async function disableAgent(agentId) {
2361
2896
  return;
2362
2897
  }
2363
2898
  const cwd = process.cwd();
2364
- const configPath = join8(cwd, CONFIG_FILE2);
2365
- if (!existsSync4(configPath)) {
2899
+ const configPath = join9(cwd, CONFIG_FILE2);
2900
+ if (!existsSync6(configPath)) {
2366
2901
  console.log(chalk11.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
2367
2902
  return;
2368
2903
  }
@@ -2418,57 +2953,765 @@ async function detectAgentsInProject() {
2418
2953
  }
2419
2954
  async function loadConfig() {
2420
2955
  const cwd = process.cwd();
2421
- const configPath = join8(cwd, CONFIG_FILE2);
2422
- if (!existsSync4(configPath)) {
2956
+ const configPath = join9(cwd, CONFIG_FILE2);
2957
+ if (!existsSync6(configPath)) {
2423
2958
  return null;
2424
2959
  }
2425
2960
  try {
2426
- const content = await readFile6(configPath, "utf-8");
2427
- return yaml3.parse(content);
2961
+ const content = await readFile8(configPath, "utf-8");
2962
+ return yaml4.parse(content);
2428
2963
  } catch {
2429
2964
  return null;
2430
2965
  }
2431
2966
  }
2432
2967
  async function saveConfig(config2) {
2433
2968
  const cwd = process.cwd();
2434
- const configPath = join8(cwd, CONFIG_FILE2);
2435
- const content = yaml3.stringify(config2);
2436
- await writeFile5(configPath, content, "utf-8");
2969
+ const configPath = join9(cwd, CONFIG_FILE2);
2970
+ const content = yaml4.stringify(config2);
2971
+ await writeFile6(configPath, content, "utf-8");
2972
+ }
2973
+
2974
+ // src/commands/check.ts
2975
+ import chalk12 from "chalk";
2976
+ import ora9 from "ora";
2977
+ import { readFile as readFile9, readdir as readdir5, stat as stat2 } from "fs/promises";
2978
+ import { join as join10 } from "path";
2979
+ import { existsSync as existsSync7 } from "fs";
2980
+ import * as yaml5 from "yaml";
2981
+ var CONFIG_FILES2 = [
2982
+ { path: "AGENTS.md", name: "AGENTS.md" },
2983
+ { path: "CLAUDE.md", name: "CLAUDE.md" },
2984
+ { path: ".github/copilot-instructions.md", name: "GitHub Copilot" },
2985
+ { path: ".windsurfrules", name: "Windsurf" },
2986
+ { path: ".clinerules", name: "Cline" },
2987
+ { path: ".goosehints", name: "Goose" },
2988
+ { path: ".zed/instructions.md", name: "Zed" }
2989
+ ];
2990
+ var CONFIG_DIRS2 = [
2991
+ { path: ".cursor/rules", name: "Cursor" },
2992
+ { path: ".lynxprompt", name: "LynxPrompt" }
2993
+ ];
2994
+ function validateMarkdown(content, filename) {
2995
+ const errors = [];
2996
+ const warnings = [];
2997
+ if (!content.trim()) {
2998
+ errors.push(`${filename}: File is empty`);
2999
+ return { errors, warnings };
3000
+ }
3001
+ if (content.trim().length < 50) {
3002
+ warnings.push(`${filename}: Content seems too short (< 50 chars)`);
3003
+ }
3004
+ if (!content.includes("#")) {
3005
+ warnings.push(`${filename}: No markdown headers found`);
3006
+ }
3007
+ const placeholders = [
3008
+ "TODO",
3009
+ "FIXME",
3010
+ "YOUR_",
3011
+ "REPLACE_",
3012
+ "[INSERT",
3013
+ "example.com"
3014
+ ];
3015
+ for (const placeholder of placeholders) {
3016
+ if (content.includes(placeholder)) {
3017
+ warnings.push(`${filename}: Contains placeholder text "${placeholder}"`);
3018
+ }
3019
+ }
3020
+ const secretPatterns = [
3021
+ /sk[_-][a-zA-Z0-9]{20,}/,
3022
+ // Stripe-like keys
3023
+ /ghp_[a-zA-Z0-9]{36}/,
3024
+ // GitHub tokens
3025
+ /api[_-]?key[_-]?=\s*[a-zA-Z0-9]{20,}/i
3026
+ ];
3027
+ for (const pattern of secretPatterns) {
3028
+ if (pattern.test(content)) {
3029
+ errors.push(`${filename}: Potential secret/API key detected - DO NOT commit secrets!`);
3030
+ break;
3031
+ }
3032
+ }
3033
+ return { errors, warnings };
3034
+ }
3035
+ async function validateLynxPromptConfig(cwd) {
3036
+ const errors = [];
3037
+ const warnings = [];
3038
+ const configPath = join10(cwd, ".lynxprompt/conf.yml");
3039
+ if (!existsSync7(configPath)) {
3040
+ return { errors, warnings };
3041
+ }
3042
+ try {
3043
+ const content = await readFile9(configPath, "utf-8");
3044
+ const config2 = yaml5.parse(content);
3045
+ if (!config2.version) {
3046
+ warnings.push(".lynxprompt/conf.yml: Missing 'version' field");
3047
+ }
3048
+ if (!config2.exporters || !Array.isArray(config2.exporters)) {
3049
+ errors.push(".lynxprompt/conf.yml: Missing or invalid 'exporters' field");
3050
+ } else if (config2.exporters.length === 0) {
3051
+ warnings.push(".lynxprompt/conf.yml: No exporters configured");
3052
+ }
3053
+ if (!config2.sources || !Array.isArray(config2.sources)) {
3054
+ errors.push(".lynxprompt/conf.yml: Missing or invalid 'sources' field");
3055
+ } else {
3056
+ for (const source of config2.sources) {
3057
+ if (source.type === "local" && source.path) {
3058
+ const sourcePath = join10(cwd, source.path);
3059
+ if (!existsSync7(sourcePath)) {
3060
+ errors.push(`.lynxprompt/conf.yml: Source path not found: ${source.path}`);
3061
+ }
3062
+ }
3063
+ }
3064
+ }
3065
+ } catch (error) {
3066
+ errors.push(`.lynxprompt/conf.yml: Invalid YAML syntax - ${error instanceof Error ? error.message : "parse error"}`);
3067
+ }
3068
+ return { errors, warnings };
3069
+ }
3070
+ function validateMdc(content, filename) {
3071
+ const errors = [];
3072
+ const warnings = [];
3073
+ if (!content.startsWith("---")) {
3074
+ warnings.push(`${filename}: Missing YAML frontmatter`);
3075
+ } else {
3076
+ const frontmatterEnd = content.indexOf("---", 3);
3077
+ if (frontmatterEnd === -1) {
3078
+ errors.push(`${filename}: Unclosed YAML frontmatter`);
3079
+ } else {
3080
+ const frontmatter = content.substring(3, frontmatterEnd).trim();
3081
+ try {
3082
+ yaml5.parse(frontmatter);
3083
+ } catch {
3084
+ errors.push(`${filename}: Invalid YAML frontmatter`);
3085
+ }
3086
+ }
3087
+ }
3088
+ const bodyStart = content.indexOf("---", 3);
3089
+ if (bodyStart !== -1) {
3090
+ const body = content.substring(bodyStart + 3).trim();
3091
+ const mdResult = validateMarkdown(body, filename);
3092
+ warnings.push(...mdResult.warnings);
3093
+ }
3094
+ return { errors, warnings };
3095
+ }
3096
+ async function checkCommand(options = {}) {
3097
+ const isCi = options.ci;
3098
+ const cwd = process.cwd();
3099
+ if (!isCi) {
3100
+ console.log();
3101
+ console.log(chalk12.cyan("\u{1F431} LynxPrompt Check"));
3102
+ console.log();
3103
+ }
3104
+ const result = {
3105
+ valid: true,
3106
+ errors: [],
3107
+ warnings: [],
3108
+ files: []
3109
+ };
3110
+ const spinner = !isCi ? ora9("Scanning for configuration files...").start() : null;
3111
+ for (const file of CONFIG_FILES2) {
3112
+ const filePath = join10(cwd, file.path);
3113
+ if (existsSync7(filePath)) {
3114
+ result.files.push(file.path);
3115
+ try {
3116
+ const content = await readFile9(filePath, "utf-8");
3117
+ const validation = validateMarkdown(content, file.path);
3118
+ result.errors.push(...validation.errors);
3119
+ result.warnings.push(...validation.warnings);
3120
+ } catch (error) {
3121
+ result.errors.push(`${file.path}: Could not read file`);
3122
+ }
3123
+ }
3124
+ }
3125
+ for (const dir of CONFIG_DIRS2) {
3126
+ const dirPath = join10(cwd, dir.path);
3127
+ if (existsSync7(dirPath)) {
3128
+ try {
3129
+ const files = await readdir5(dirPath);
3130
+ for (const file of files) {
3131
+ const filePath = join10(dirPath, file);
3132
+ const fileStat = await stat2(filePath);
3133
+ if (fileStat.isFile()) {
3134
+ result.files.push(`${dir.path}/${file}`);
3135
+ const content = await readFile9(filePath, "utf-8");
3136
+ if (file.endsWith(".mdc")) {
3137
+ const validation = validateMdc(content, `${dir.path}/${file}`);
3138
+ result.errors.push(...validation.errors);
3139
+ result.warnings.push(...validation.warnings);
3140
+ } else if (file.endsWith(".md")) {
3141
+ const validation = validateMarkdown(content, `${dir.path}/${file}`);
3142
+ result.errors.push(...validation.errors);
3143
+ result.warnings.push(...validation.warnings);
3144
+ }
3145
+ }
3146
+ }
3147
+ } catch {
3148
+ }
3149
+ }
3150
+ }
3151
+ const lynxpromptValidation = await validateLynxPromptConfig(cwd);
3152
+ result.errors.push(...lynxpromptValidation.errors);
3153
+ result.warnings.push(...lynxpromptValidation.warnings);
3154
+ spinner?.stop();
3155
+ result.valid = result.errors.length === 0;
3156
+ if (isCi) {
3157
+ if (!result.valid) {
3158
+ console.error("\u2717 Validation failed");
3159
+ for (const error of result.errors) {
3160
+ console.error(` ${error}`);
3161
+ }
3162
+ process.exit(1);
3163
+ } else if (result.files.length === 0) {
3164
+ console.error("\u2717 No configuration files found");
3165
+ process.exit(1);
3166
+ } else {
3167
+ console.log("\u2713 Validation passed");
3168
+ if (result.warnings.length > 0) {
3169
+ console.log(` (${result.warnings.length} warning${result.warnings.length === 1 ? "" : "s"})`);
3170
+ }
3171
+ process.exit(0);
3172
+ }
3173
+ } else {
3174
+ if (result.files.length === 0) {
3175
+ console.log(chalk12.yellow("\u26A0 No AI configuration files found."));
3176
+ console.log();
3177
+ console.log(chalk12.gray("Run 'lynxp wizard' to create a configuration."));
3178
+ return;
3179
+ }
3180
+ console.log(chalk12.green(`\u2713 Found ${result.files.length} configuration file${result.files.length === 1 ? "" : "s"}:`));
3181
+ for (const file of result.files) {
3182
+ console.log(chalk12.gray(` ${file}`));
3183
+ }
3184
+ console.log();
3185
+ if (result.errors.length > 0) {
3186
+ console.log(chalk12.red(`\u2717 ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}:`));
3187
+ for (const error of result.errors) {
3188
+ console.log(chalk12.red(` ${error}`));
3189
+ }
3190
+ console.log();
3191
+ }
3192
+ if (result.warnings.length > 0) {
3193
+ console.log(chalk12.yellow(`\u26A0 ${result.warnings.length} warning${result.warnings.length === 1 ? "" : "s"}:`));
3194
+ for (const warning of result.warnings) {
3195
+ console.log(chalk12.yellow(` ${warning}`));
3196
+ }
3197
+ console.log();
3198
+ }
3199
+ if (result.valid) {
3200
+ console.log(chalk12.green("\u2705 Validation passed!"));
3201
+ } else {
3202
+ console.log(chalk12.red("\u274C Validation failed. Fix the errors above."));
3203
+ }
3204
+ console.log();
3205
+ }
3206
+ }
3207
+
3208
+ // src/commands/diff.ts
3209
+ import chalk13 from "chalk";
3210
+ import ora10 from "ora";
3211
+ import { readFile as readFile10 } from "fs/promises";
3212
+ import { join as join11 } from "path";
3213
+ import { existsSync as existsSync8 } from "fs";
3214
+ function computeDiff(oldText, newText) {
3215
+ const oldLines = oldText.split("\n");
3216
+ const newLines = newText.split("\n");
3217
+ const result = [];
3218
+ const lcs = longestCommonSubsequence(oldLines, newLines);
3219
+ let oldIndex = 0;
3220
+ let newIndex = 0;
3221
+ let lcsIndex = 0;
3222
+ while (oldIndex < oldLines.length || newIndex < newLines.length) {
3223
+ if (lcsIndex < lcs.length && oldIndex < oldLines.length && oldLines[oldIndex] === lcs[lcsIndex]) {
3224
+ if (newIndex < newLines.length && newLines[newIndex] === lcs[lcsIndex]) {
3225
+ result.push({ type: "same", line: oldLines[oldIndex] });
3226
+ oldIndex++;
3227
+ newIndex++;
3228
+ lcsIndex++;
3229
+ } else {
3230
+ result.push({ type: "add", line: newLines[newIndex] });
3231
+ newIndex++;
3232
+ }
3233
+ } else if (oldIndex < oldLines.length && (lcsIndex >= lcs.length || oldLines[oldIndex] !== lcs[lcsIndex])) {
3234
+ result.push({ type: "remove", line: oldLines[oldIndex] });
3235
+ oldIndex++;
3236
+ } else if (newIndex < newLines.length) {
3237
+ result.push({ type: "add", line: newLines[newIndex] });
3238
+ newIndex++;
3239
+ }
3240
+ }
3241
+ return result;
3242
+ }
3243
+ function longestCommonSubsequence(a, b) {
3244
+ const m = a.length;
3245
+ const n = b.length;
3246
+ const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
3247
+ for (let i2 = 1; i2 <= m; i2++) {
3248
+ for (let j2 = 1; j2 <= n; j2++) {
3249
+ if (a[i2 - 1] === b[j2 - 1]) {
3250
+ dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
3251
+ } else {
3252
+ dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
3253
+ }
3254
+ }
3255
+ }
3256
+ const lcs = [];
3257
+ let i = m;
3258
+ let j = n;
3259
+ while (i > 0 && j > 0) {
3260
+ if (a[i - 1] === b[j - 1]) {
3261
+ lcs.unshift(a[i - 1]);
3262
+ i--;
3263
+ j--;
3264
+ } else if (dp[i - 1][j] > dp[i][j - 1]) {
3265
+ i--;
3266
+ } else {
3267
+ j--;
3268
+ }
3269
+ }
3270
+ return lcs;
3271
+ }
3272
+ function formatDiff(diff, contextLines = 3) {
3273
+ const output = [];
3274
+ let lastPrintedIndex = -1;
3275
+ let inHunk = false;
3276
+ const changeIndices = diff.map((d, i) => d.type !== "same" ? i : -1).filter((i) => i !== -1);
3277
+ if (changeIndices.length === 0) {
3278
+ return chalk13.gray(" (no changes)");
3279
+ }
3280
+ for (let i = 0; i < diff.length; i++) {
3281
+ const item = diff[i];
3282
+ const nearChange = changeIndices.some((ci) => Math.abs(ci - i) <= contextLines);
3283
+ if (nearChange) {
3284
+ if (lastPrintedIndex !== -1 && i - lastPrintedIndex > 1) {
3285
+ output.push(chalk13.gray(" ..."));
3286
+ }
3287
+ if (item.type === "add") {
3288
+ output.push(chalk13.green(`+ ${item.line}`));
3289
+ } else if (item.type === "remove") {
3290
+ output.push(chalk13.red(`- ${item.line}`));
3291
+ } else {
3292
+ output.push(chalk13.gray(` ${item.line}`));
3293
+ }
3294
+ lastPrintedIndex = i;
3295
+ }
3296
+ }
3297
+ return output.join("\n");
3298
+ }
3299
+ function getDiffStats(diff) {
3300
+ return {
3301
+ added: diff.filter((d) => d.type === "add").length,
3302
+ removed: diff.filter((d) => d.type === "remove").length,
3303
+ unchanged: diff.filter((d) => d.type === "same").length
3304
+ };
3305
+ }
3306
+ async function diffCommand(blueprintId, options = {}) {
3307
+ console.log();
3308
+ console.log(chalk13.cyan("\u{1F431} LynxPrompt Diff"));
3309
+ console.log();
3310
+ const cwd = process.cwd();
3311
+ if (options.local) {
3312
+ await diffLocal(cwd);
3313
+ return;
3314
+ }
3315
+ if (!blueprintId) {
3316
+ console.log(chalk13.red("\u2717 Please provide a blueprint ID to compare with."));
3317
+ console.log();
3318
+ console.log(chalk13.gray("Usage:"));
3319
+ console.log(chalk13.gray(" lynxp diff <blueprint-id> Compare local with remote blueprint"));
3320
+ console.log(chalk13.gray(" lynxp diff --local Compare .lynxprompt/rules/ with exports"));
3321
+ return;
3322
+ }
3323
+ if (!isAuthenticated()) {
3324
+ console.log(chalk13.yellow("\u26A0 Not logged in. Some blueprints may not be accessible."));
3325
+ console.log(chalk13.gray("Run 'lynxp login' to authenticate."));
3326
+ console.log();
3327
+ }
3328
+ const spinner = ora10("Fetching blueprint...").start();
3329
+ try {
3330
+ const { blueprint } = await api.getBlueprint(blueprintId);
3331
+ spinner.stop();
3332
+ if (!blueprint || !blueprint.content) {
3333
+ console.log(chalk13.red(`\u2717 Blueprint not found or has no content: ${blueprintId}`));
3334
+ return;
3335
+ }
3336
+ console.log(chalk13.green(`\u2713 Blueprint: ${blueprint.name || blueprintId}`));
3337
+ if (blueprint.description) {
3338
+ console.log(chalk13.gray(` ${blueprint.description}`));
3339
+ }
3340
+ console.log();
3341
+ const localPaths = [
3342
+ "AGENTS.md",
3343
+ "CLAUDE.md",
3344
+ ".cursor/rules/project.mdc",
3345
+ ".github/copilot-instructions.md",
3346
+ ".windsurfrules"
3347
+ ];
3348
+ let localContent = null;
3349
+ let localPath = null;
3350
+ for (const path of localPaths) {
3351
+ const fullPath = join11(cwd, path);
3352
+ if (existsSync8(fullPath)) {
3353
+ try {
3354
+ localContent = await readFile10(fullPath, "utf-8");
3355
+ localPath = path;
3356
+ break;
3357
+ } catch {
3358
+ }
3359
+ }
3360
+ }
3361
+ if (!localContent) {
3362
+ console.log(chalk13.yellow("\u26A0 No local AI configuration file found."));
3363
+ console.log(chalk13.gray("Run 'lynxp wizard' to create one, or 'lynxp pull' to download the blueprint."));
3364
+ return;
3365
+ }
3366
+ console.log(chalk13.gray(`Comparing with: ${localPath}`));
3367
+ console.log();
3368
+ const diff = computeDiff(blueprint.content, localContent);
3369
+ const stats = getDiffStats(diff);
3370
+ if (stats.added === 0 && stats.removed === 0) {
3371
+ console.log(chalk13.green("\u2713 Files are identical!"));
3372
+ } else {
3373
+ console.log(chalk13.gray("Changes (remote \u2192 local):"));
3374
+ console.log();
3375
+ console.log(formatDiff(diff));
3376
+ console.log();
3377
+ console.log(chalk13.gray(`Summary: ${chalk13.green(`+${stats.added}`)} ${chalk13.red(`-${stats.removed}`)} lines changed`));
3378
+ }
3379
+ console.log();
3380
+ } catch (error) {
3381
+ spinner.stop();
3382
+ if (error instanceof ApiRequestError) {
3383
+ if (error.statusCode === 401) {
3384
+ console.log(chalk13.red("\u2717 Authentication required. Run 'lynxp login' first."));
3385
+ } else if (error.statusCode === 404) {
3386
+ console.log(chalk13.red(`\u2717 Blueprint not found: ${blueprintId}`));
3387
+ } else if (error.statusCode === 403) {
3388
+ console.log(chalk13.red("\u2717 Access denied to this blueprint."));
3389
+ } else {
3390
+ console.log(chalk13.red(`\u2717 API error: ${error.message}`));
3391
+ }
3392
+ } else {
3393
+ console.log(chalk13.red("\u2717 Failed to fetch blueprint"));
3394
+ if (error instanceof Error) {
3395
+ console.log(chalk13.gray(` ${error.message}`));
3396
+ }
3397
+ }
3398
+ }
3399
+ }
3400
+ async function diffLocal(cwd) {
3401
+ const rulesDir = join11(cwd, ".lynxprompt/rules");
3402
+ if (!existsSync8(rulesDir)) {
3403
+ console.log(chalk13.yellow("\u26A0 No .lynxprompt/rules/ directory found."));
3404
+ console.log(chalk13.gray("Run 'lynxp init' to set up the advanced workflow, or 'lynxp wizard' for simple file generation."));
3405
+ return;
3406
+ }
3407
+ console.log(chalk13.gray("Comparing .lynxprompt/rules/ with exported files..."));
3408
+ console.log();
3409
+ const rulesPath = join11(rulesDir, "agents.md");
3410
+ if (!existsSync8(rulesPath)) {
3411
+ console.log(chalk13.yellow("\u26A0 No rules files found in .lynxprompt/rules/"));
3412
+ return;
3413
+ }
3414
+ let rulesContent;
3415
+ try {
3416
+ rulesContent = await readFile10(rulesPath, "utf-8");
3417
+ } catch {
3418
+ console.log(chalk13.red("\u2717 Could not read .lynxprompt/rules/agents.md"));
3419
+ return;
3420
+ }
3421
+ const exportedFiles = [
3422
+ { path: "AGENTS.md", name: "AGENTS.md" },
3423
+ { path: ".cursor/rules/lynxprompt-rules.mdc", name: "Cursor" }
3424
+ ];
3425
+ let hasChanges = false;
3426
+ for (const file of exportedFiles) {
3427
+ const filePath = join11(cwd, file.path);
3428
+ if (existsSync8(filePath)) {
3429
+ try {
3430
+ const exportedContent = await readFile10(filePath, "utf-8");
3431
+ let compareContent = exportedContent;
3432
+ if (file.path.endsWith(".mdc")) {
3433
+ const frontmatterEnd = exportedContent.indexOf("---", 3);
3434
+ if (frontmatterEnd !== -1) {
3435
+ compareContent = exportedContent.substring(frontmatterEnd + 3).trim();
3436
+ }
3437
+ }
3438
+ compareContent = compareContent.replace(/^# AI Coding Rules\n\n> Generated by \[LynxPrompt\].*\n\n/m, "").trim();
3439
+ const diff = computeDiff(rulesContent.trim(), compareContent);
3440
+ const stats = getDiffStats(diff);
3441
+ if (stats.added > 0 || stats.removed > 0) {
3442
+ hasChanges = true;
3443
+ console.log(chalk13.yellow(`\u26A0 ${file.name} differs from source:`));
3444
+ console.log(formatDiff(diff));
3445
+ console.log(chalk13.gray(` ${chalk13.green(`+${stats.added}`)} ${chalk13.red(`-${stats.removed}`)} lines`));
3446
+ console.log();
3447
+ } else {
3448
+ console.log(chalk13.green(`\u2713 ${file.name} is in sync`));
3449
+ }
3450
+ } catch {
3451
+ }
3452
+ }
3453
+ }
3454
+ if (!hasChanges) {
3455
+ console.log();
3456
+ console.log(chalk13.green("\u2713 All exported files are in sync with .lynxprompt/rules/"));
3457
+ } else {
3458
+ console.log();
3459
+ console.log(chalk13.gray("Run 'lynxp sync' to update exported files from .lynxprompt/rules/"));
3460
+ }
3461
+ console.log();
3462
+ }
3463
+
3464
+ // src/commands/link.ts
3465
+ import chalk14 from "chalk";
3466
+ import ora11 from "ora";
3467
+ import prompts6 from "prompts";
3468
+ import { join as join12 } from "path";
3469
+ import { existsSync as existsSync9 } from "fs";
3470
+ function getSourceFromVisibility2(visibility) {
3471
+ switch (visibility) {
3472
+ case "PUBLIC":
3473
+ return "marketplace";
3474
+ case "TEAM":
3475
+ return "team";
3476
+ case "PRIVATE":
3477
+ return "private";
3478
+ default:
3479
+ return "marketplace";
3480
+ }
3481
+ }
3482
+ async function linkCommand(file, blueprintId, options = {}) {
3483
+ const cwd = process.cwd();
3484
+ if (options.list) {
3485
+ await listTrackedBlueprints(cwd);
3486
+ return;
3487
+ }
3488
+ if (!file) {
3489
+ console.log(chalk14.red("\u2717 Please provide a file path to link."));
3490
+ console.log();
3491
+ console.log(chalk14.gray("Usage:"));
3492
+ console.log(chalk14.gray(" lynxp link <file> <blueprint-id> Link a local file to a cloud blueprint"));
3493
+ console.log(chalk14.gray(" lynxp link --list List all tracked blueprints"));
3494
+ console.log();
3495
+ console.log(chalk14.gray("Example:"));
3496
+ console.log(chalk14.gray(" lynxp link AGENTS.md bp_abc123"));
3497
+ return;
3498
+ }
3499
+ if (!blueprintId) {
3500
+ console.log(chalk14.red("\u2717 Please provide a blueprint ID to link to."));
3501
+ console.log();
3502
+ console.log(chalk14.gray("Usage: lynxp link <file> <blueprint-id>"));
3503
+ console.log(chalk14.gray("Example: lynxp link AGENTS.md bp_abc123"));
3504
+ console.log();
3505
+ console.log(chalk14.gray("To find blueprint IDs:"));
3506
+ console.log(chalk14.gray(" lynxp list - Show your blueprints"));
3507
+ console.log(chalk14.gray(" lynxp search <query> - Search marketplace"));
3508
+ return;
3509
+ }
3510
+ const filePath = join12(cwd, file);
3511
+ if (!existsSync9(filePath)) {
3512
+ console.log(chalk14.red(`\u2717 File not found: ${file}`));
3513
+ return;
3514
+ }
3515
+ const existing = await findBlueprintByFile(cwd, file);
3516
+ if (existing) {
3517
+ console.log(chalk14.yellow(`\u26A0 This file is already linked to: ${existing.id}`));
3518
+ const { proceed } = await prompts6({
3519
+ type: "confirm",
3520
+ name: "proceed",
3521
+ message: "Replace the existing link?",
3522
+ initial: false
3523
+ });
3524
+ if (!proceed) {
3525
+ console.log(chalk14.gray("Cancelled."));
3526
+ return;
3527
+ }
3528
+ }
3529
+ if (!isAuthenticated()) {
3530
+ console.log(
3531
+ chalk14.yellow("Not logged in. Run 'lynxp login' to authenticate.")
3532
+ );
3533
+ process.exit(1);
3534
+ }
3535
+ const spinner = ora11(`Fetching blueprint ${chalk14.cyan(blueprintId)}...`).start();
3536
+ try {
3537
+ const { blueprint } = await api.getBlueprint(blueprintId);
3538
+ spinner.stop();
3539
+ const source = getSourceFromVisibility2(blueprint.visibility);
3540
+ const isMarketplace = source === "marketplace";
3541
+ console.log();
3542
+ console.log(chalk14.cyan(`\u{1F431} Blueprint: ${chalk14.bold(blueprint.name)}`));
3543
+ if (blueprint.description) {
3544
+ console.log(chalk14.gray(` ${blueprint.description}`));
3545
+ }
3546
+ console.log(chalk14.gray(` Visibility: ${blueprint.visibility}`));
3547
+ console.log();
3548
+ if (isMarketplace) {
3549
+ console.log(chalk14.yellow("\u26A0 This is a marketplace blueprint."));
3550
+ console.log(chalk14.gray(" Your local changes will NOT sync back to the cloud."));
3551
+ console.log(chalk14.gray(" To make changes, you'll need to create your own copy."));
3552
+ console.log();
3553
+ }
3554
+ const { confirm } = await prompts6({
3555
+ type: "confirm",
3556
+ name: "confirm",
3557
+ message: `Link ${chalk14.cyan(file)} to ${chalk14.cyan(blueprint.name)}?`,
3558
+ initial: true
3559
+ });
3560
+ if (!confirm) {
3561
+ console.log(chalk14.gray("Cancelled."));
3562
+ return;
3563
+ }
3564
+ await linkBlueprint(cwd, file, blueprint.id, blueprint.name, source);
3565
+ console.log();
3566
+ console.log(chalk14.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
3567
+ console.log();
3568
+ console.log(chalk14.gray("Next steps:"));
3569
+ console.log(chalk14.gray(` \u2022 Run 'lynxp pull ${blueprintId}' to update local file from cloud`));
3570
+ console.log(chalk14.gray(` \u2022 Run 'lynxp diff ${blueprintId}' to see differences`));
3571
+ console.log(chalk14.gray(` \u2022 Run 'lynxp status' to see all tracked blueprints`));
3572
+ if (!isMarketplace) {
3573
+ console.log(chalk14.gray(` \u2022 Run 'lynxp push ${file}' to push local changes to cloud`));
3574
+ }
3575
+ console.log();
3576
+ } catch (error) {
3577
+ spinner.stop();
3578
+ if (error instanceof ApiRequestError) {
3579
+ if (error.statusCode === 404) {
3580
+ console.log(chalk14.red(`\u2717 Blueprint not found: ${blueprintId}`));
3581
+ console.log(chalk14.gray(" Make sure the ID is correct. Use 'lynxp list' or 'lynxp search' to find blueprints."));
3582
+ } else if (error.statusCode === 403) {
3583
+ console.log(chalk14.red("\u2717 You don't have access to this blueprint."));
3584
+ } else {
3585
+ console.log(chalk14.red(`\u2717 Error: ${error.message}`));
3586
+ }
3587
+ } else {
3588
+ console.log(chalk14.red("\u2717 An unexpected error occurred."));
3589
+ }
3590
+ }
3591
+ }
3592
+ async function unlinkCommand(file) {
3593
+ const cwd = process.cwd();
3594
+ if (!file) {
3595
+ console.log(chalk14.red("\u2717 Please provide a file path to unlink."));
3596
+ console.log();
3597
+ console.log(chalk14.gray("Usage: lynxp unlink <file>"));
3598
+ console.log(chalk14.gray("Example: lynxp unlink AGENTS.md"));
3599
+ return;
3600
+ }
3601
+ const tracked = await findBlueprintByFile(cwd, file);
3602
+ if (!tracked) {
3603
+ console.log(chalk14.yellow(`\u26A0 File is not linked to any blueprint: ${file}`));
3604
+ return;
3605
+ }
3606
+ console.log();
3607
+ console.log(chalk14.cyan(`Currently linked to: ${tracked.id}`));
3608
+ console.log(chalk14.gray(` Name: ${tracked.name}`));
3609
+ console.log(chalk14.gray(` Source: ${tracked.source}`));
3610
+ console.log();
3611
+ const { confirm } = await prompts6({
3612
+ type: "confirm",
3613
+ name: "confirm",
3614
+ message: `Unlink ${chalk14.cyan(file)} from ${chalk14.cyan(tracked.name)}?`,
3615
+ initial: true
3616
+ });
3617
+ if (!confirm) {
3618
+ console.log(chalk14.gray("Cancelled."));
3619
+ return;
3620
+ }
3621
+ const success = await untrackBlueprint(cwd, file);
3622
+ if (success) {
3623
+ console.log();
3624
+ console.log(chalk14.green(`\u2705 Unlinked: ${file}`));
3625
+ console.log(chalk14.gray(" The file is now a standalone local file."));
3626
+ console.log(chalk14.gray(" It will no longer receive updates from the cloud blueprint."));
3627
+ console.log();
3628
+ } else {
3629
+ console.log(chalk14.red("\u2717 Failed to unlink file."));
3630
+ }
3631
+ }
3632
+ async function listTrackedBlueprints(cwd) {
3633
+ console.log();
3634
+ console.log(chalk14.cyan("\u{1F431} Tracked Blueprints"));
3635
+ console.log();
3636
+ const status = await checkSyncStatus(cwd);
3637
+ if (status.length === 0) {
3638
+ console.log(chalk14.gray("No blueprints are currently tracked."));
3639
+ console.log();
3640
+ console.log(chalk14.gray("To track a blueprint:"));
3641
+ console.log(chalk14.gray(" lynxp pull <blueprint-id> Download and track a blueprint"));
3642
+ console.log(chalk14.gray(" lynxp link <file> <id> Link an existing file to a blueprint"));
3643
+ return;
3644
+ }
3645
+ for (const { blueprint, localModified, fileExists: fileExists2 } of status) {
3646
+ const statusIcon = !fileExists2 ? chalk14.red("\u2717") : localModified ? chalk14.yellow("\u25CF") : chalk14.green("\u2713");
3647
+ const sourceLabel = {
3648
+ marketplace: chalk14.gray("[marketplace]"),
3649
+ team: chalk14.blue("[team]"),
3650
+ private: chalk14.green("[private]"),
3651
+ local: chalk14.gray("[local]")
3652
+ }[blueprint.source];
3653
+ console.log(`${statusIcon} ${chalk14.cyan(blueprint.file)}`);
3654
+ console.log(` ${sourceLabel} ${blueprint.name}`);
3655
+ console.log(` ${chalk14.gray(`ID: ${blueprint.id}`)}`);
3656
+ if (!fileExists2) {
3657
+ console.log(chalk14.red(` \u26A0 File not found`));
3658
+ } else if (localModified) {
3659
+ console.log(chalk14.yellow(` \u26A0 Local changes detected`));
3660
+ }
3661
+ console.log();
3662
+ }
3663
+ console.log(chalk14.gray("Legend:"));
3664
+ console.log(chalk14.gray(` ${chalk14.green("\u2713")} In sync ${chalk14.yellow("\u25CF")} Modified locally ${chalk14.red("\u2717")} Missing`));
3665
+ console.log();
2437
3666
  }
2438
3667
 
2439
3668
  // src/index.ts
2440
3669
  var program = new Command();
2441
- program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version("0.1.0");
3670
+ program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version("0.2.0");
3671
+ program.command("wizard").description("Generate AI IDE configuration (recommended for most users)").option("-n, --name <name>", "Project name").option("-d, --description <description>", "Project description").option("-s, --stack <stack>", "Tech stack (comma-separated)").option("-f, --format <format>", "Output format: agents, cursor, or comma-separated for multiple").option("-p, --platforms <platforms>", "Alias for --format (deprecated)").option("--persona <persona>", "AI persona (fullstack, backend, frontend, devops, data, security)").option("--boundaries <level>", "Boundary preset (conservative, standard, permissive)").option("-y, --yes", "Skip prompts, use defaults (generates AGENTS.md)").action(wizardCommand);
3672
+ program.command("check").description("Validate AI configuration files (for CI/CD)").option("--ci", "CI mode - exit codes only (0=pass, 1=fail)").action(checkCommand);
3673
+ program.command("status").description("Show current AI configuration and tracked blueprints").action(statusCommand);
3674
+ program.command("pull <id>").description("Download and track a blueprint from the marketplace").option("-o, --output <path>", "Output directory", ".").option("-y, --yes", "Overwrite existing files without prompting").option("--preview", "Preview content without downloading").option("--no-track", "Don't track the blueprint for future syncs").action(pullCommand);
3675
+ program.command("search <query>").description("Search public blueprints in the marketplace").option("-l, --limit <number>", "Number of results", "20").action(searchCommand);
3676
+ 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);
3677
+ 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);
3678
+ program.command("unlink <file>").description("Disconnect a local file from its cloud blueprint").action(unlinkCommand);
3679
+ program.command("diff [blueprint-id]").description("Show changes between local and remote blueprint").option("--local", "Compare .lynxprompt/rules/ with exported files").action(diffCommand);
3680
+ 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);
3681
+ 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);
3682
+ program.command("agents [action] [agent]").description("Manage AI agents (list, enable, disable, detect)").option("-i, --interactive", "Interactive agent selection").action(agentsCommand);
2442
3683
  program.command("login").description("Authenticate with LynxPrompt (opens browser)").action(loginCommand);
2443
3684
  program.command("logout").description("Log out and remove stored credentials").action(logoutCommand);
2444
3685
  program.command("whoami").description("Show current authenticated user").action(whoamiCommand);
2445
- program.command("init").description("Initialize LynxPrompt in this directory (auto-detects existing files)").option("-y, --yes", "Skip prompts and use defaults").option("-f, --force", "Re-initialize even if already initialized").action(initCommand);
2446
- program.command("wizard").description("Interactive wizard to generate AI IDE configuration").option("-n, --name <name>", "Project name").option("-d, --description <description>", "Project description").option("-s, --stack <stack>", "Tech stack (comma-separated)").option("-p, --platforms <platforms>", "Target platforms (comma-separated)").option("--persona <persona>", "AI persona/role").option("--boundaries <level>", "Boundary preset (conservative, standard, permissive)").option("--preset <preset>", "Use an agent preset (test-agent, docs-agent, etc.)").option("-y, --yes", "Skip prompts and use defaults").action(wizardCommand);
2447
- program.command("list").description("List your blueprints").option("-l, --limit <number>", "Number of results", "20").option("-v, --visibility <visibility>", "Filter by visibility (PRIVATE, TEAM, PUBLIC, all)").action(listCommand);
2448
- program.command("pull <id>").description("Download a blueprint to the current directory").option("-o, --output <path>", "Output directory", ".").option("-y, --yes", "Overwrite existing files without prompting").action(pullCommand);
2449
- program.command("search <query>").description("Search public blueprints").option("-l, --limit <number>", "Number of results", "20").action(searchCommand);
2450
- program.command("status").description("Show current AI config status in this directory").action(statusCommand);
2451
- program.command("sync").description("Sync rules to all configured AI agents").option("--dry-run", "Preview changes without writing files").option("-f, --force", "Skip prompts (for CI/automation)").action(syncCommand);
2452
- program.command("agents [action] [agent]").description("Manage AI agents (list, enable, disable, detect)").option("-i, --interactive", "Interactive agent selection").action(agentsCommand);
2453
3686
  program.addHelpText(
2454
3687
  "beforeAll",
2455
3688
  `
2456
- ${chalk12.cyan("\u{1F431} LynxPrompt CLI")} ${chalk12.gray("(also available as: lynxp)")}
2457
- ${chalk12.gray("Generate AI IDE configuration files from your terminal")}
3689
+ ${chalk15.cyan("\u{1F431} LynxPrompt CLI")} ${chalk15.gray("(also available as: lynxp)")}
3690
+ ${chalk15.gray("Generate AI IDE configuration files from your terminal")}
2458
3691
  `
2459
3692
  );
2460
3693
  program.addHelpText(
2461
3694
  "after",
2462
3695
  `
2463
- ${chalk12.gray("Examples:")}
2464
- ${chalk12.cyan("$ lynxp init")} ${chalk12.gray("Initialize LynxPrompt in this directory")}
2465
- ${chalk12.cyan("$ lynxp sync")} ${chalk12.gray("Sync rules to all configured agents")}
2466
- ${chalk12.cyan("$ lynxp agents")} ${chalk12.gray("List and manage AI agents")}
2467
- ${chalk12.cyan("$ lynxp wizard")} ${chalk12.gray("Start interactive configuration wizard")}
2468
- ${chalk12.cyan("$ lynxp pull bp_abc123")} ${chalk12.gray("Download a blueprint")}
2469
- ${chalk12.cyan("$ lynxp search nextjs")} ${chalk12.gray("Search public blueprints")}
2470
-
2471
- ${chalk12.gray("Documentation: https://lynxprompt.com/docs/cli")}
3696
+ ${chalk15.cyan("Quick Start:")}
3697
+ ${chalk15.white("$ lynxp wizard")} ${chalk15.gray("Generate config interactively")}
3698
+ ${chalk15.white("$ lynxp wizard -y")} ${chalk15.gray("Generate AGENTS.md with defaults")}
3699
+ ${chalk15.white("$ lynxp wizard -f cursor")} ${chalk15.gray("Generate .cursor/rules/")}
3700
+
3701
+ ${chalk15.cyan("Marketplace:")}
3702
+ ${chalk15.white("$ lynxp search nextjs")} ${chalk15.gray("Search blueprints")}
3703
+ ${chalk15.white("$ lynxp pull bp_abc123")} ${chalk15.gray("Download and track a blueprint")}
3704
+ ${chalk15.white("$ lynxp link --list")} ${chalk15.gray("Show tracked blueprints")}
3705
+
3706
+ ${chalk15.cyan("Blueprint Tracking:")}
3707
+ ${chalk15.white("$ lynxp link AGENTS.md bp_xyz")} ${chalk15.gray("Link existing file to blueprint")}
3708
+ ${chalk15.white("$ lynxp unlink AGENTS.md")} ${chalk15.gray("Disconnect from cloud")}
3709
+ ${chalk15.white("$ lynxp diff bp_abc123")} ${chalk15.gray("Show changes vs cloud version")}
3710
+
3711
+ ${chalk15.cyan("CI/CD:")}
3712
+ ${chalk15.white("$ lynxp check --ci")} ${chalk15.gray("Validate config (exit code)")}
3713
+
3714
+ ${chalk15.gray("Docs: https://lynxprompt.com/docs/cli")}
2472
3715
  `
2473
3716
  );
2474
3717
  program.parse();