lynxprompt 0.2.1 → 0.3.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 chalk15 from "chalk";
5
+ import chalk16 from "chalk";
6
6
 
7
7
  // src/commands/login.ts
8
8
  import chalk from "chalk";
@@ -125,6 +125,11 @@ var ApiClient = class {
125
125
  );
126
126
  }
127
127
  async getBlueprint(id) {
128
+ if (id.startsWith("usr_")) {
129
+ const blueprint = await this.request(`/api/blueprints/${id}`);
130
+ const visibility = blueprint.isPublic ? "PUBLIC" : "PRIVATE";
131
+ return { blueprint: { ...blueprint, visibility, type: blueprint.tier || "GENERIC" } };
132
+ }
128
133
  const apiId = id.startsWith("bp_") ? id : `bp_${id}`;
129
134
  return this.request(`/api/v1/blueprints/${apiId}`);
130
135
  }
@@ -135,6 +140,19 @@ var ApiClient = class {
135
140
  });
136
141
  return this.request(`/api/blueprints?${params}`);
137
142
  }
143
+ async createBlueprint(data) {
144
+ return this.request("/api/v1/blueprints", {
145
+ method: "POST",
146
+ body: JSON.stringify(data)
147
+ });
148
+ }
149
+ async updateBlueprint(id, data) {
150
+ const apiId = id.startsWith("bp_") ? id : `bp_${id}`;
151
+ return this.request(`/api/v1/blueprints/${apiId}`, {
152
+ method: "PUT",
153
+ body: JSON.stringify(data)
154
+ });
155
+ }
138
156
  };
139
157
  var ApiRequestError = class extends Error {
140
158
  constructor(message, statusCode, response) {
@@ -474,6 +492,15 @@ async function hasLocalChanges(cwd, tracked) {
474
492
  return false;
475
493
  }
476
494
  }
495
+ async function updateChecksum(cwd, file, content) {
496
+ const config2 = await loadBlueprints(cwd);
497
+ const blueprint = config2.blueprints.find((b) => b.file === file);
498
+ if (blueprint) {
499
+ blueprint.checksum = calculateChecksum(content);
500
+ blueprint.pulledAt = (/* @__PURE__ */ new Date()).toISOString();
501
+ await saveBlueprints(cwd, config2);
502
+ }
503
+ }
477
504
  async function untrackBlueprint(cwd, file) {
478
505
  const config2 = await loadBlueprints(cwd);
479
506
  const initialCount = config2.blueprints.length;
@@ -541,10 +568,14 @@ function getSourceFromVisibility(visibility) {
541
568
  }
542
569
  }
543
570
  async function pullCommand(id, options) {
544
- if (!isAuthenticated()) {
571
+ const isPublicBlueprint = id.startsWith("usr_");
572
+ if (!isPublicBlueprint && !isAuthenticated()) {
545
573
  console.log(
546
574
  chalk5.yellow("Not logged in. Run 'lynxp login' to authenticate.")
547
575
  );
576
+ console.log(
577
+ chalk5.gray("Note: Public marketplace blueprints (usr_...) can be downloaded without login.")
578
+ );
548
579
  process.exit(1);
549
580
  }
550
581
  const cwd = process.cwd();
@@ -766,10 +797,191 @@ function handleApiError2(error) {
766
797
  process.exit(1);
767
798
  }
768
799
 
769
- // src/commands/init.ts
800
+ // src/commands/push.ts
770
801
  import chalk6 from "chalk";
771
- import prompts2 from "prompts";
772
802
  import ora5 from "ora";
803
+ import fs from "fs";
804
+ import path from "path";
805
+ import prompts2 from "prompts";
806
+ async function pushCommand(fileArg, options) {
807
+ const cwd = process.cwd();
808
+ if (!isAuthenticated()) {
809
+ console.log(chalk6.yellow("You need to be logged in to push blueprints."));
810
+ console.log(chalk6.gray("Run 'lynxp login' to authenticate."));
811
+ process.exit(1);
812
+ }
813
+ const file = fileArg || findDefaultFile();
814
+ if (!file) {
815
+ console.log(chalk6.red("No AI configuration file found."));
816
+ console.log(
817
+ chalk6.gray("Specify a file or run in a directory with AGENTS.md, CLAUDE.md, etc.")
818
+ );
819
+ process.exit(1);
820
+ }
821
+ if (!fs.existsSync(file)) {
822
+ console.log(chalk6.red(`File not found: ${file}`));
823
+ process.exit(1);
824
+ }
825
+ const content = fs.readFileSync(file, "utf-8");
826
+ const filename = path.basename(file);
827
+ const linked = await findBlueprintByFile(cwd, file);
828
+ if (linked) {
829
+ await updateBlueprint(cwd, file, linked.id, content, options);
830
+ } else {
831
+ await createOrLinkBlueprint(cwd, file, filename, content, options);
832
+ }
833
+ }
834
+ async function updateBlueprint(cwd, file, blueprintId, content, options) {
835
+ console.log(chalk6.cyan(`
836
+ \u{1F4E4} Updating blueprint ${chalk6.bold(blueprintId)}...`));
837
+ console.log(chalk6.gray(` File: ${file}`));
838
+ if (!options.yes) {
839
+ const confirm = await prompts2({
840
+ type: "confirm",
841
+ name: "value",
842
+ message: `Push changes to ${blueprintId}?`,
843
+ initial: true
844
+ });
845
+ if (!confirm.value) {
846
+ console.log(chalk6.yellow("Push cancelled."));
847
+ return;
848
+ }
849
+ }
850
+ const spinner = ora5("Pushing changes...").start();
851
+ try {
852
+ const result = await api.updateBlueprint(blueprintId, { content });
853
+ spinner.succeed("Blueprint updated!");
854
+ await updateChecksum(cwd, file, content);
855
+ console.log();
856
+ console.log(chalk6.green(`\u2705 Successfully updated ${chalk6.bold(result.blueprint.name)}`));
857
+ console.log(chalk6.gray(` ID: ${blueprintId}`));
858
+ console.log(chalk6.gray(` View: https://lynxprompt.com/templates/${blueprintId.replace("bp_", "")}`));
859
+ } catch (error) {
860
+ spinner.fail("Failed to update blueprint");
861
+ handleError(error);
862
+ }
863
+ }
864
+ async function createOrLinkBlueprint(cwd, file, filename, content, options) {
865
+ console.log(chalk6.cyan("\n\u{1F4E4} Push new blueprint"));
866
+ console.log(chalk6.gray(` File: ${file}`));
867
+ let name = options.name;
868
+ let description = options.description;
869
+ let visibility = options.visibility || "PRIVATE";
870
+ let tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
871
+ if (!options.yes) {
872
+ const responses = await prompts2([
873
+ {
874
+ type: name ? null : "text",
875
+ name: "name",
876
+ message: "Blueprint name:",
877
+ initial: filename.replace(/\.(md|mdc|json|yml|yaml)$/, ""),
878
+ validate: (v) => v.length > 0 || "Name is required"
879
+ },
880
+ {
881
+ type: description ? null : "text",
882
+ name: "description",
883
+ message: "Description:",
884
+ initial: ""
885
+ },
886
+ {
887
+ type: "select",
888
+ name: "visibility",
889
+ message: "Visibility:",
890
+ choices: [
891
+ { title: "Private (only you)", value: "PRIVATE" },
892
+ { title: "Team (your team members)", value: "TEAM" },
893
+ { title: "Public (visible to everyone)", value: "PUBLIC" }
894
+ ],
895
+ initial: 0
896
+ },
897
+ {
898
+ type: "text",
899
+ name: "tags",
900
+ message: "Tags (comma-separated):",
901
+ initial: ""
902
+ }
903
+ ]);
904
+ if (!responses.name && !name) {
905
+ console.log(chalk6.yellow("Push cancelled."));
906
+ return;
907
+ }
908
+ name = name || responses.name;
909
+ description = description || responses.description || "";
910
+ visibility = responses.visibility || visibility;
911
+ tags = responses.tags ? responses.tags.split(",").map((t) => t.trim()).filter(Boolean) : tags;
912
+ }
913
+ if (!name) {
914
+ name = filename.replace(/\.(md|mdc|json|yml|yaml)$/, "");
915
+ }
916
+ const spinner = ora5("Creating blueprint...").start();
917
+ try {
918
+ const result = await api.createBlueprint({
919
+ name,
920
+ description: description || "",
921
+ content,
922
+ visibility,
923
+ tags
924
+ });
925
+ spinner.succeed("Blueprint created!");
926
+ await trackBlueprint(cwd, {
927
+ id: result.blueprint.id,
928
+ name: result.blueprint.name,
929
+ file,
930
+ content,
931
+ source: "private"
932
+ });
933
+ console.log();
934
+ console.log(chalk6.green(`\u2705 Created blueprint ${chalk6.bold(result.blueprint.name)}`));
935
+ console.log(chalk6.gray(` ID: ${result.blueprint.id}`));
936
+ console.log(chalk6.gray(` Visibility: ${visibility}`));
937
+ if (visibility === "PUBLIC") {
938
+ console.log(chalk6.gray(` View: https://lynxprompt.com/templates/${result.blueprint.id.replace("bp_", "")}`));
939
+ }
940
+ console.log();
941
+ console.log(chalk6.cyan("The file is now linked. Future 'lynxp push' will update this blueprint."));
942
+ } catch (error) {
943
+ spinner.fail("Failed to create blueprint");
944
+ handleError(error);
945
+ }
946
+ }
947
+ function findDefaultFile() {
948
+ const candidates = [
949
+ "AGENTS.md",
950
+ "CLAUDE.md",
951
+ ".cursor/rules/project.mdc",
952
+ ".github/copilot-instructions.md",
953
+ ".windsurfrules",
954
+ "AIDER.md",
955
+ "GEMINI.md",
956
+ ".clinerules"
957
+ ];
958
+ for (const candidate of candidates) {
959
+ if (fs.existsSync(candidate)) {
960
+ return candidate;
961
+ }
962
+ }
963
+ return null;
964
+ }
965
+ function handleError(error) {
966
+ if (error instanceof ApiRequestError) {
967
+ console.error(chalk6.red(`Error: ${error.message}`));
968
+ if (error.statusCode === 401) {
969
+ console.error(chalk6.gray("Your session may have expired. Run 'lynxp login' to re-authenticate."));
970
+ } else if (error.statusCode === 403) {
971
+ console.error(chalk6.gray("You don't have permission to modify this blueprint."));
972
+ } else if (error.statusCode === 404) {
973
+ console.error(chalk6.gray("Blueprint not found. It may have been deleted."));
974
+ }
975
+ } else {
976
+ console.error(chalk6.red("An unexpected error occurred."));
977
+ }
978
+ process.exit(1);
979
+ }
980
+
981
+ // src/commands/init.ts
982
+ import chalk7 from "chalk";
983
+ import prompts3 from "prompts";
984
+ import ora6 from "ora";
773
985
  import { writeFile as writeFile3, mkdir as mkdir3, readFile as readFile4 } from "fs/promises";
774
986
  import { join as join5, dirname as dirname3, basename } from "path";
775
987
  import { existsSync as existsSync3 } from "fs";
@@ -1127,9 +1339,9 @@ function countSections(content) {
1127
1339
  const headings = content.match(/^#{1,6}\s+.+$/gm);
1128
1340
  return headings ? headings.length : content.trim().length > 0 ? 1 : 0;
1129
1341
  }
1130
- function safeReadFile(path) {
1342
+ function safeReadFile(path2) {
1131
1343
  try {
1132
- return readFileSync(path, "utf-8");
1344
+ return readFileSync(path2, "utf-8");
1133
1345
  } catch {
1134
1346
  return null;
1135
1347
  }
@@ -1158,7 +1370,7 @@ function formatDetectionResults(result) {
1158
1370
  }
1159
1371
 
1160
1372
  // src/utils/detect.ts
1161
- import { readFile as readFile3, access as access3 } from "fs/promises";
1373
+ import { readFile as readFile3, access as access2 } from "fs/promises";
1162
1374
  import { join as join4 } from "path";
1163
1375
  var JS_FRAMEWORK_PATTERNS = {
1164
1376
  nextjs: ["next"],
@@ -1379,9 +1591,9 @@ async function detectProject(cwd) {
1379
1591
  }
1380
1592
  return detected.stack.length > 0 || detected.name ? detected : null;
1381
1593
  }
1382
- async function fileExists(path) {
1594
+ async function fileExists(path2) {
1383
1595
  try {
1384
- await access3(path);
1596
+ await access2(path2);
1385
1597
  return true;
1386
1598
  } catch {
1387
1599
  return false;
@@ -1553,14 +1765,14 @@ This exports your rules to the configured agent formats (AGENTS.md, .cursor/rule
1553
1765
  }
1554
1766
  async function initCommand(options) {
1555
1767
  console.log();
1556
- console.log(chalk6.cyan("\u{1F431} LynxPrompt Init"));
1557
- console.log(chalk6.gray("Advanced mode: Multi-editor rule management"));
1768
+ console.log(chalk7.cyan("\u{1F431} LynxPrompt Init"));
1769
+ console.log(chalk7.gray("Advanced mode: Multi-editor rule management"));
1558
1770
  console.log();
1559
1771
  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."));
1772
+ console.log(chalk7.yellow("\u{1F4A1} Tip: Most users should use 'lynxp wizard' instead."));
1773
+ console.log(chalk7.gray(" The wizard generates files directly without the .lynxprompt/ folder."));
1562
1774
  console.log();
1563
- const { proceed } = await prompts2({
1775
+ const { proceed } = await prompts3({
1564
1776
  type: "confirm",
1565
1777
  name: "proceed",
1566
1778
  message: "Continue with advanced setup?",
@@ -1568,7 +1780,7 @@ async function initCommand(options) {
1568
1780
  });
1569
1781
  if (!proceed) {
1570
1782
  console.log();
1571
- console.log(chalk6.gray("Run 'lynxp wizard' for simple file generation."));
1783
+ console.log(chalk7.gray("Run 'lynxp wizard' for simple file generation."));
1572
1784
  return;
1573
1785
  }
1574
1786
  console.log();
@@ -1579,15 +1791,15 @@ async function initCommand(options) {
1579
1791
  const configPath = join5(cwd, LYNXPROMPT_CONFIG);
1580
1792
  const rulesDir = join5(cwd, LYNXPROMPT_RULES);
1581
1793
  if (existsSync3(configPath) && !options.force) {
1582
- console.log(chalk6.yellow("LynxPrompt is already initialized in this project."));
1583
- console.log(chalk6.gray(`Config: ${LYNXPROMPT_CONFIG}`));
1584
- console.log(chalk6.gray(`Rules: ${LYNXPROMPT_RULES}/`));
1794
+ console.log(chalk7.yellow("LynxPrompt is already initialized in this project."));
1795
+ console.log(chalk7.gray(`Config: ${LYNXPROMPT_CONFIG}`));
1796
+ console.log(chalk7.gray(`Rules: ${LYNXPROMPT_RULES}/`));
1585
1797
  console.log();
1586
- console.log(chalk6.gray("Run 'lynxp sync' to export rules to your agents."));
1587
- console.log(chalk6.gray("Run 'lynxp wizard' to generate new configurations."));
1798
+ console.log(chalk7.gray("Run 'lynxp sync' to export rules to your agents."));
1799
+ console.log(chalk7.gray("Run 'lynxp wizard' to generate new configurations."));
1588
1800
  return;
1589
1801
  }
1590
- const spinner = ora5("Scanning project...").start();
1802
+ const spinner = ora6("Scanning project...").start();
1591
1803
  const [projectInfo, agentDetection] = await Promise.all([
1592
1804
  detectProject(cwd),
1593
1805
  Promise.resolve(detectAgents(cwd))
@@ -1595,28 +1807,28 @@ async function initCommand(options) {
1595
1807
  const existingFiles = await scanForExistingFiles(cwd);
1596
1808
  spinner.stop();
1597
1809
  if (projectInfo) {
1598
- console.log(chalk6.green("\u2713 Detected project:"));
1599
- if (projectInfo.name) console.log(chalk6.gray(` Name: ${projectInfo.name}`));
1600
- if (projectInfo.stack.length > 0) console.log(chalk6.gray(` Stack: ${projectInfo.stack.join(", ")}`));
1601
- if (projectInfo.packageManager) console.log(chalk6.gray(` Package manager: ${projectInfo.packageManager}`));
1810
+ console.log(chalk7.green("\u2713 Detected project:"));
1811
+ if (projectInfo.name) console.log(chalk7.gray(` Name: ${projectInfo.name}`));
1812
+ if (projectInfo.stack.length > 0) console.log(chalk7.gray(` Stack: ${projectInfo.stack.join(", ")}`));
1813
+ if (projectInfo.packageManager) console.log(chalk7.gray(` Package manager: ${projectInfo.packageManager}`));
1602
1814
  console.log();
1603
1815
  }
1604
1816
  if (agentDetection.detected.length > 0) {
1605
- console.log(chalk6.green(`\u2713 Detected ${agentDetection.detected.length} AI agent${agentDetection.detected.length === 1 ? "" : "s"}:`));
1817
+ console.log(chalk7.green(`\u2713 Detected ${agentDetection.detected.length} AI agent${agentDetection.detected.length === 1 ? "" : "s"}:`));
1606
1818
  for (const detected of agentDetection.detected) {
1607
- const rules = detected.ruleCount > 0 ? chalk6.gray(` (${detected.ruleCount} sections)`) : "";
1608
- console.log(` ${chalk6.cyan("\u2022")} ${detected.agent.name}${rules}`);
1819
+ const rules = detected.ruleCount > 0 ? chalk7.gray(` (${detected.ruleCount} sections)`) : "";
1820
+ console.log(` ${chalk7.cyan("\u2022")} ${detected.agent.name}${rules}`);
1609
1821
  }
1610
1822
  console.log();
1611
1823
  }
1612
1824
  if (existingFiles.length > 0) {
1613
- console.log(chalk6.green("\u2713 Found existing AI configuration files:"));
1825
+ console.log(chalk7.green("\u2713 Found existing AI configuration files:"));
1614
1826
  for (const file of existingFiles) {
1615
- console.log(` ${chalk6.cyan(file.path)} ${chalk6.gray(`(${file.agent})`)}`);
1827
+ console.log(` ${chalk7.cyan(file.path)} ${chalk7.gray(`(${file.agent})`)}`);
1616
1828
  }
1617
1829
  console.log();
1618
1830
  if (!options.yes) {
1619
- const { action } = await prompts2({
1831
+ const { action } = await prompts3({
1620
1832
  type: "select",
1621
1833
  name: "action",
1622
1834
  message: "What would you like to do?",
@@ -1627,7 +1839,7 @@ async function initCommand(options) {
1627
1839
  ]
1628
1840
  });
1629
1841
  if (action === "cancel" || !action) {
1630
- console.log(chalk6.gray("Cancelled."));
1842
+ console.log(chalk7.gray("Cancelled."));
1631
1843
  return;
1632
1844
  }
1633
1845
  if (action === "import") {
@@ -1638,20 +1850,20 @@ async function initCommand(options) {
1638
1850
  const ruleName = file.path.replace(/^\./, "").replace(/\//g, "-").replace(/\.md$/, "") + ".md";
1639
1851
  const rulePath = join5(rulesDir, ruleName);
1640
1852
  await writeFile3(rulePath, file.content, "utf-8");
1641
- console.log(chalk6.gray(` Imported: ${file.path} \u2192 .lynxprompt/rules/${ruleName}`));
1853
+ console.log(chalk7.gray(` Imported: ${file.path} \u2192 .lynxprompt/rules/${ruleName}`));
1642
1854
  importedCount++;
1643
1855
  }
1644
1856
  }
1645
1857
  if (importedCount === 0) {
1646
1858
  const starterPath = join5(rulesDir, "agents.md");
1647
1859
  await writeFile3(starterPath, createStarterAgentsMd(projectName), "utf-8");
1648
- console.log(chalk6.gray(" Created starter: .lynxprompt/rules/agents.md"));
1860
+ console.log(chalk7.gray(" Created starter: .lynxprompt/rules/agents.md"));
1649
1861
  }
1650
1862
  } else {
1651
1863
  await mkdir3(rulesDir, { recursive: true });
1652
1864
  const starterPath = join5(rulesDir, "agents.md");
1653
1865
  await writeFile3(starterPath, createStarterAgentsMd(projectName), "utf-8");
1654
- console.log(chalk6.gray("Created starter: .lynxprompt/rules/agents.md"));
1866
+ console.log(chalk7.gray("Created starter: .lynxprompt/rules/agents.md"));
1655
1867
  }
1656
1868
  } else {
1657
1869
  await mkdir3(rulesDir, { recursive: true });
@@ -1664,30 +1876,30 @@ async function initCommand(options) {
1664
1876
  }
1665
1877
  }
1666
1878
  } else {
1667
- console.log(chalk6.gray("No existing AI configuration files found."));
1879
+ console.log(chalk7.gray("No existing AI configuration files found."));
1668
1880
  console.log();
1669
1881
  if (!options.yes) {
1670
- const { create } = await prompts2({
1882
+ const { create } = await prompts3({
1671
1883
  type: "confirm",
1672
1884
  name: "create",
1673
1885
  message: "Create a starter template?",
1674
1886
  initial: true
1675
1887
  });
1676
1888
  if (!create) {
1677
- console.log(chalk6.gray("Cancelled."));
1889
+ console.log(chalk7.gray("Cancelled."));
1678
1890
  return;
1679
1891
  }
1680
1892
  }
1681
1893
  await mkdir3(rulesDir, { recursive: true });
1682
1894
  const starterPath = join5(rulesDir, "agents.md");
1683
1895
  await writeFile3(starterPath, createStarterAgentsMd(projectName), "utf-8");
1684
- console.log(chalk6.gray("Created: .lynxprompt/rules/agents.md"));
1896
+ console.log(chalk7.gray("Created: .lynxprompt/rules/agents.md"));
1685
1897
  }
1686
1898
  let exporters = [];
1687
1899
  if (agentDetection.detected.length > 0) {
1688
1900
  exporters = agentDetection.detected.map((d) => d.agent.id);
1689
1901
  if (agentDetection.detected.length > 3 && !options.yes) {
1690
- const { selected } = await prompts2({
1902
+ const { selected } = await prompts3({
1691
1903
  type: "multiselect",
1692
1904
  name: "selected",
1693
1905
  message: "Select agents to enable:",
@@ -1706,7 +1918,7 @@ async function initCommand(options) {
1706
1918
  exporters = ["agents"];
1707
1919
  if (!options.yes) {
1708
1920
  const popular = getPopularAgents();
1709
- const { selected } = await prompts2({
1921
+ const { selected } = await prompts3({
1710
1922
  type: "multiselect",
1711
1923
  name: "selected",
1712
1924
  message: "Select AI agents to sync to:",
@@ -1723,7 +1935,7 @@ async function initCommand(options) {
1723
1935
  }
1724
1936
  }
1725
1937
  }
1726
- console.log(chalk6.gray(`Enabling ${exporters.length} exporter${exporters.length === 1 ? "" : "s"}: ${exporters.join(", ")}`));
1938
+ console.log(chalk7.gray(`Enabling ${exporters.length} exporter${exporters.length === 1 ? "" : "s"}: ${exporters.join(", ")}`));
1727
1939
  await mkdir3(dirname3(configPath), { recursive: true });
1728
1940
  await writeFile3(configPath, createDefaultConfig(exporters), "utf-8");
1729
1941
  const readmePath = join5(lynxpromptDir, "README.md");
@@ -1735,24 +1947,24 @@ async function initCommand(options) {
1735
1947
  `;
1736
1948
  await writeFile3(gitignorePath, gitignoreContent, "utf-8");
1737
1949
  console.log();
1738
- console.log(chalk6.green("\u2705 LynxPrompt initialized!"));
1950
+ console.log(chalk7.green("\u2705 LynxPrompt initialized!"));
1739
1951
  console.log();
1740
- console.log(chalk6.gray("Created:"));
1741
- console.log(chalk6.gray(` ${LYNXPROMPT_CONFIG} - Configuration`));
1742
- console.log(chalk6.gray(` ${LYNXPROMPT_RULES}/ - Your rules (edit here)`));
1952
+ console.log(chalk7.gray("Created:"));
1953
+ console.log(chalk7.gray(` ${LYNXPROMPT_CONFIG} - Configuration`));
1954
+ console.log(chalk7.gray(` ${LYNXPROMPT_RULES}/ - Your rules (edit here)`));
1743
1955
  console.log();
1744
- console.log(chalk6.cyan("Next steps:"));
1745
- console.log(chalk6.gray(" 1. Edit your rules in .lynxprompt/rules/"));
1746
- console.log(chalk6.gray(" 2. Run 'lynxp sync' to export to your AI agents"));
1747
- console.log(chalk6.gray(" 3. Or run 'lynxp agents' to manage which agents to sync to"));
1956
+ console.log(chalk7.cyan("Next steps:"));
1957
+ console.log(chalk7.gray(" 1. Edit your rules in .lynxprompt/rules/"));
1958
+ console.log(chalk7.gray(" 2. Run 'lynxp sync' to export to your AI agents"));
1959
+ console.log(chalk7.gray(" 3. Or run 'lynxp agents' to manage which agents to sync to"));
1748
1960
  console.log();
1749
1961
  }
1750
1962
 
1751
1963
  // src/commands/wizard.ts
1752
- import chalk7 from "chalk";
1753
- import prompts3 from "prompts";
1754
- import ora6 from "ora";
1755
- import { writeFile as writeFile4, mkdir as mkdir4, access as access5 } from "fs/promises";
1964
+ import chalk8 from "chalk";
1965
+ import prompts4 from "prompts";
1966
+ import ora7 from "ora";
1967
+ import { writeFile as writeFile4, mkdir as mkdir4, access as access3 } from "fs/promises";
1756
1968
  import { join as join6, dirname as dirname4 } from "path";
1757
1969
 
1758
1970
  // src/utils/generator.ts
@@ -2033,101 +2245,137 @@ function generateFileContent(options, platform) {
2033
2245
  // src/commands/wizard.ts
2034
2246
  var OUTPUT_FORMATS = [
2035
2247
  {
2036
- title: "AGENTS.md (Universal)",
2248
+ title: "\u{1F310} AGENTS.md",
2037
2249
  value: "agents",
2038
- description: "Works with Claude Code, GitHub Copilot, Aider, and most AI editors",
2250
+ description: "Universal format - Claude, Copilot, Aider, & more",
2039
2251
  recommended: true
2040
2252
  },
2041
2253
  {
2042
- title: "Cursor (.cursor/rules/)",
2254
+ title: "\u{1F5B1}\uFE0F Cursor",
2043
2255
  value: "cursor",
2044
- description: "Cursor IDE with MDC format"
2256
+ description: ".cursor/rules/ with MDC format"
2045
2257
  },
2046
2258
  {
2047
- title: "Multiple formats",
2259
+ title: "\u{1F30A} Windsurf",
2260
+ value: "windsurf",
2261
+ description: ".windsurfrules configuration"
2262
+ },
2263
+ {
2264
+ title: "\u{1F916} Claude Code",
2265
+ value: "claude",
2266
+ description: "CLAUDE.md for Claude AI"
2267
+ },
2268
+ {
2269
+ title: "\u{1F4E6} Multiple",
2048
2270
  value: "multiple",
2049
- description: "Select multiple AI editors to generate for"
2271
+ description: "Generate for multiple AI editors"
2050
2272
  }
2051
2273
  ];
2052
2274
  var TECH_STACKS = [
2053
- { title: "TypeScript", value: "typescript" },
2054
- { title: "JavaScript", value: "javascript" },
2055
- { title: "Python", value: "python" },
2056
- { title: "Go", value: "go" },
2057
- { title: "Rust", value: "rust" },
2058
- { title: "Java", value: "java" },
2059
- { title: "C#/.NET", value: "csharp" },
2060
- { title: "Ruby", value: "ruby" },
2061
- { title: "PHP", value: "php" },
2062
- { title: "Swift", value: "swift" }
2275
+ { title: "\u{1F537} TypeScript", value: "typescript" },
2276
+ { title: "\u{1F7E1} JavaScript", value: "javascript" },
2277
+ { title: "\u{1F40D} Python", value: "python" },
2278
+ { title: "\u{1F535} Go", value: "go" },
2279
+ { title: "\u{1F980} Rust", value: "rust" },
2280
+ { title: "\u2615 Java", value: "java" },
2281
+ { title: "\u{1F49C} C#/.NET", value: "csharp" },
2282
+ { title: "\u{1F48E} Ruby", value: "ruby" },
2283
+ { title: "\u{1F418} PHP", value: "php" },
2284
+ { title: "\u{1F34E} Swift", value: "swift" }
2063
2285
  ];
2064
2286
  var FRAMEWORKS = [
2065
- { title: "React", value: "react" },
2066
- { title: "Next.js", value: "nextjs" },
2067
- { title: "Vue.js", value: "vue" },
2068
- { title: "Angular", value: "angular" },
2069
- { title: "Svelte", value: "svelte" },
2070
- { title: "Express", value: "express" },
2071
- { title: "FastAPI", value: "fastapi" },
2072
- { title: "Django", value: "django" },
2073
- { title: "Flask", value: "flask" },
2074
- { title: "Spring Boot", value: "spring" },
2075
- { title: "Rails", value: "rails" },
2076
- { title: "Laravel", value: "laravel" }
2287
+ { title: "\u269B\uFE0F React", value: "react" },
2288
+ { title: "\u25B2 Next.js", value: "nextjs" },
2289
+ { title: "\u{1F49A} Vue.js", value: "vue" },
2290
+ { title: "\u{1F170}\uFE0F Angular", value: "angular" },
2291
+ { title: "\u{1F525} Svelte", value: "svelte" },
2292
+ { title: "\u{1F682} Express", value: "express" },
2293
+ { title: "\u26A1 FastAPI", value: "fastapi" },
2294
+ { title: "\u{1F3B8} Django", value: "django" },
2295
+ { title: "\u{1F9EA} Flask", value: "flask" },
2296
+ { title: "\u{1F343} Spring", value: "spring" },
2297
+ { title: "\u{1F48E} Rails", value: "rails" },
2298
+ { title: "\u{1F534} Laravel", value: "laravel" },
2299
+ { title: "\u{1F3D7}\uFE0F NestJS", value: "nestjs" },
2300
+ { title: "\u26A1 Vite", value: "vite" },
2301
+ { title: "\u{1F4F1} React Native", value: "react-native" }
2077
2302
  ];
2078
2303
  var PLATFORMS = [
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" },
2082
- { title: "GitHub Copilot", value: "copilot", filename: ".github/copilot-instructions.md" },
2083
- { title: "Windsurf (.windsurfrules)", value: "windsurf", filename: ".windsurfrules" },
2084
- { title: "Zed", value: "zed", filename: ".zed/instructions.md" }
2304
+ { title: "\u{1F310} AGENTS.md (Universal)", value: "agents", filename: "AGENTS.md" },
2305
+ { title: "\u{1F5B1}\uFE0F Cursor", value: "cursor", filename: ".cursor/rules/project.mdc" },
2306
+ { title: "\u{1F916} Claude Code", value: "claude", filename: "CLAUDE.md" },
2307
+ { title: "\u{1F419} GitHub Copilot", value: "copilot", filename: ".github/copilot-instructions.md" },
2308
+ { title: "\u{1F30A} Windsurf", value: "windsurf", filename: ".windsurfrules" },
2309
+ { title: "\u26A1 Zed", value: "zed", filename: ".zed/instructions.md" },
2310
+ { title: "\u{1F916} Cline", value: "cline", filename: ".clinerules" }
2085
2311
  ];
2086
2312
  var PERSONAS = [
2087
- { title: "Full-Stack Developer - Complete application setups", value: "fullstack" },
2088
- { title: "Backend Developer - APIs, databases, microservices", value: "backend" },
2089
- { title: "Frontend Developer - UI, components, styling", value: "frontend" },
2090
- { title: "DevOps Engineer - Infrastructure, CI/CD, containers", value: "devops" },
2091
- { title: "Data Engineer - Pipelines, ETL, databases", value: "data" },
2092
- { title: "Security Engineer - Secure code, vulnerabilities", value: "security" },
2093
- { title: "Custom...", value: "custom" }
2313
+ { title: "\u{1F9D1}\u200D\u{1F4BB} Full-Stack Developer", value: "fullstack", description: "Complete application development" },
2314
+ { title: "\u2699\uFE0F Backend Developer", value: "backend", description: "APIs, databases, services" },
2315
+ { title: "\u{1F3A8} Frontend Developer", value: "frontend", description: "UI, components, styling" },
2316
+ { title: "\u{1F680} DevOps Engineer", value: "devops", description: "Infrastructure, CI/CD" },
2317
+ { title: "\u{1F4CA} Data Engineer", value: "data", description: "Pipelines, ETL, analytics" },
2318
+ { title: "\u{1F512} Security Engineer", value: "security", description: "Secure code, auditing" },
2319
+ { title: "\u270F\uFE0F Custom...", value: "custom", description: "Define your own" }
2094
2320
  ];
2095
2321
  var BOUNDARY_PRESETS = [
2096
2322
  {
2097
- title: "Standard - Balance of freedom and safety (recommended)",
2323
+ title: "\u{1F7E2} Standard",
2098
2324
  value: "standard",
2325
+ description: "Balanced freedom & safety",
2099
2326
  always: ["Read any file", "Modify files in src/", "Run build/test/lint", "Create test files"],
2100
2327
  askFirst: ["Add new dependencies", "Modify config files", "Create new modules"],
2101
2328
  never: ["Delete production data", "Modify .env secrets", "Force push"]
2102
2329
  },
2103
2330
  {
2104
- title: "Conservative - Ask before most changes",
2331
+ title: "\u{1F7E1} Conservative",
2105
2332
  value: "conservative",
2333
+ description: "Ask before most changes",
2106
2334
  always: ["Read any file", "Run lint/format commands"],
2107
2335
  askFirst: ["Modify any file", "Add dependencies", "Create files", "Run tests"],
2108
2336
  never: ["Delete files", "Modify .env", "Push to git"]
2109
2337
  },
2110
2338
  {
2111
- title: "Permissive - AI can modify freely within src/",
2339
+ title: "\u{1F7E0} Permissive",
2112
2340
  value: "permissive",
2341
+ description: "AI can modify freely",
2113
2342
  always: ["Modify any file in src/", "Run any script", "Add dependencies", "Create files"],
2114
2343
  askFirst: ["Modify root configs", "Delete directories"],
2115
2344
  never: ["Modify .env", "Access external APIs without confirmation"]
2116
2345
  }
2117
2346
  ];
2347
+ function showStep(current, total, title) {
2348
+ const progress = "\u25CF".repeat(current) + "\u25CB".repeat(total - current);
2349
+ console.log();
2350
+ console.log(chalk8.cyan(` ${progress} Step ${current}/${total}: ${title}`));
2351
+ console.log();
2352
+ }
2353
+ function printBox(lines, color = chalk8.gray) {
2354
+ const maxLen = Math.max(...lines.map((l) => l.replace(/\x1b\[[0-9;]*m/g, "").length));
2355
+ const top = "\u250C" + "\u2500".repeat(maxLen + 2) + "\u2510";
2356
+ const bottom = "\u2514" + "\u2500".repeat(maxLen + 2) + "\u2518";
2357
+ console.log(color(top));
2358
+ for (const line of lines) {
2359
+ const stripped = line.replace(/\x1b\[[0-9;]*m/g, "");
2360
+ const padding = " ".repeat(maxLen - stripped.length);
2361
+ console.log(color("\u2502 ") + line + padding + color(" \u2502"));
2362
+ }
2363
+ console.log(color(bottom));
2364
+ }
2118
2365
  async function wizardCommand(options) {
2119
2366
  console.log();
2120
- console.log(chalk7.cyan("\u{1F431} LynxPrompt Wizard"));
2121
- console.log(chalk7.gray("Generate AI IDE configuration in seconds"));
2367
+ console.log(chalk8.cyan.bold(" \u{1F431} LynxPrompt Wizard"));
2368
+ console.log(chalk8.gray(" Generate AI IDE configuration in seconds"));
2122
2369
  console.log();
2123
2370
  const detected = await detectProject(process.cwd());
2124
2371
  if (detected) {
2125
- console.log(chalk7.green("\u2713 Detected project:"));
2126
- if (detected.name) console.log(chalk7.gray(` Name: ${detected.name}`));
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}`));
2129
- if (detected.commands.build) console.log(chalk7.gray(` Build: ${detected.commands.build}`));
2130
- if (detected.commands.test) console.log(chalk7.gray(` Test: ${detected.commands.test}`));
2372
+ const detectedInfo = [
2373
+ chalk8.green("\u2713 Project detected")
2374
+ ];
2375
+ if (detected.name) detectedInfo.push(chalk8.gray(` Name: ${detected.name}`));
2376
+ if (detected.stack.length > 0) detectedInfo.push(chalk8.gray(` Stack: ${detected.stack.join(", ")}`));
2377
+ if (detected.packageManager) detectedInfo.push(chalk8.gray(` Package manager: ${detected.packageManager}`));
2378
+ printBox(detectedInfo, chalk8.gray);
2131
2379
  console.log();
2132
2380
  }
2133
2381
  let config2;
@@ -2152,29 +2400,30 @@ async function wizardCommand(options) {
2152
2400
  } else {
2153
2401
  config2 = await runInteractiveWizard(options, detected);
2154
2402
  }
2155
- const spinner = ora6("Generating configuration...").start();
2403
+ const spinner = ora7("Generating configuration...").start();
2156
2404
  try {
2157
2405
  const files = generateConfig(config2);
2158
2406
  spinner.stop();
2159
2407
  console.log();
2160
- console.log(chalk7.green("\u2705 Generated:"));
2408
+ console.log(chalk8.green.bold(" \u2705 Generated:"));
2409
+ console.log();
2161
2410
  for (const [filename, content] of Object.entries(files)) {
2162
2411
  const outputPath = join6(process.cwd(), filename);
2163
2412
  let exists = false;
2164
2413
  try {
2165
- await access5(outputPath);
2414
+ await access3(outputPath);
2166
2415
  exists = true;
2167
2416
  } catch {
2168
2417
  }
2169
2418
  if (exists && !options.yes) {
2170
- const response = await prompts3({
2419
+ const response = await prompts4({
2171
2420
  type: "confirm",
2172
2421
  name: "overwrite",
2173
2422
  message: `${filename} already exists. Overwrite?`,
2174
2423
  initial: false
2175
2424
  });
2176
2425
  if (!response.overwrite) {
2177
- console.log(chalk7.yellow(` Skipped: ${filename}`));
2426
+ console.log(chalk8.yellow(` \u23ED\uFE0F Skipped: ${filename}`));
2178
2427
  continue;
2179
2428
  }
2180
2429
  }
@@ -2183,141 +2432,159 @@ async function wizardCommand(options) {
2183
2432
  await mkdir4(dir, { recursive: true });
2184
2433
  }
2185
2434
  await writeFile4(outputPath, content, "utf-8");
2186
- console.log(` ${chalk7.cyan(filename)}`);
2435
+ console.log(` ${chalk8.cyan("\u2192")} ${chalk8.bold(filename)}`);
2187
2436
  }
2188
2437
  console.log();
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"));
2438
+ printBox([
2439
+ chalk8.gray("Your AI assistant will now follow these instructions."),
2440
+ "",
2441
+ chalk8.gray("Next steps:"),
2442
+ chalk8.cyan(" lynxp check ") + chalk8.gray("Validate configuration"),
2443
+ chalk8.cyan(" lynxp push ") + chalk8.gray("Sync to cloud"),
2444
+ chalk8.cyan(" lynxp status ") + chalk8.gray("View current setup")
2445
+ ], chalk8.gray);
2195
2446
  console.log();
2196
2447
  } catch (error) {
2197
2448
  spinner.fail("Failed to generate files");
2198
- console.error(chalk7.red("\n\u2717 An error occurred while generating configuration files."));
2449
+ console.error(chalk8.red("\n\u2717 An error occurred while generating configuration files."));
2199
2450
  if (error instanceof Error) {
2200
- console.error(chalk7.gray(` ${error.message}`));
2451
+ console.error(chalk8.gray(` ${error.message}`));
2201
2452
  }
2202
- console.error(chalk7.gray("\nTry running with --yes flag for default settings."));
2453
+ console.error(chalk8.gray("\nTry running with --yes flag for default settings."));
2203
2454
  process.exit(1);
2204
2455
  }
2205
2456
  }
2206
2457
  async function runInteractiveWizard(options, detected) {
2207
2458
  const answers = {};
2459
+ const totalSteps = 5;
2460
+ const promptConfig = {
2461
+ onCancel: () => {
2462
+ console.log(chalk8.yellow("\n Cancelled. Run 'lynxp wizard' anytime to restart.\n"));
2463
+ process.exit(0);
2464
+ }
2465
+ };
2466
+ showStep(1, totalSteps, "Output Format");
2208
2467
  let platforms;
2209
2468
  if (options.format) {
2210
2469
  platforms = options.format.split(",").map((f) => f.trim());
2470
+ console.log(chalk8.gray(` Using format from flag: ${platforms.join(", ")}`));
2211
2471
  } else {
2212
- const formatResponse = await prompts3({
2472
+ const formatResponse = await prompts4({
2213
2473
  type: "select",
2214
2474
  name: "format",
2215
- message: "Select output format:",
2475
+ message: chalk8.white("Where will you use this?"),
2216
2476
  choices: OUTPUT_FORMATS.map((f) => ({
2217
- title: f.recommended ? `${f.title} ${chalk7.green("(recommended)")}` : f.title,
2477
+ title: f.recommended ? `${f.title} ${chalk8.green.bold("\u2605 recommended")}` : f.title,
2218
2478
  value: f.value,
2219
- description: f.description
2479
+ description: chalk8.gray(f.description)
2220
2480
  })),
2221
- initial: 0
2222
- // AGENTS.md is default
2223
- });
2481
+ initial: 0,
2482
+ hint: chalk8.gray("\u2191\u2193 navigate \u2022 enter select")
2483
+ }, promptConfig);
2224
2484
  if (formatResponse.format === "multiple") {
2225
- const platformResponse = await prompts3({
2485
+ console.log();
2486
+ const platformResponse = await prompts4({
2226
2487
  type: "multiselect",
2227
2488
  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
- });
2489
+ message: chalk8.white("Select AI editors:"),
2490
+ choices: PLATFORMS.map((p) => ({
2491
+ title: p.title,
2492
+ value: p.value
2493
+ })),
2494
+ hint: chalk8.gray("space select \u2022 a toggle all \u2022 enter confirm"),
2495
+ min: 1,
2496
+ instructions: false
2497
+ }, promptConfig);
2233
2498
  platforms = platformResponse.platforms || ["agents"];
2234
2499
  } else {
2235
2500
  platforms = [formatResponse.format || "agents"];
2236
2501
  }
2237
2502
  }
2238
2503
  answers.platforms = platforms;
2239
- const nameResponse = await prompts3({
2504
+ showStep(2, totalSteps, "Project Info");
2505
+ const nameResponse = await prompts4({
2240
2506
  type: "text",
2241
2507
  name: "name",
2242
- message: "Project name:",
2243
- initial: options.name || detected?.name || "my-project"
2244
- });
2508
+ message: chalk8.white("Project name:"),
2509
+ initial: options.name || detected?.name || "my-project",
2510
+ hint: chalk8.gray("Used in the generated config header")
2511
+ }, promptConfig);
2245
2512
  answers.name = nameResponse.name || "my-project";
2246
- const descResponse = await prompts3({
2513
+ const descResponse = await prompts4({
2247
2514
  type: "text",
2248
2515
  name: "description",
2249
- message: "Brief description (optional):",
2250
- initial: options.description || ""
2251
- });
2516
+ message: chalk8.white("Brief description:"),
2517
+ initial: options.description || "",
2518
+ hint: chalk8.gray("optional - helps AI understand context")
2519
+ }, promptConfig);
2252
2520
  answers.description = descResponse.description || "";
2521
+ showStep(3, totalSteps, "Tech Stack");
2253
2522
  const allStackOptions = [...TECH_STACKS, ...FRAMEWORKS];
2254
2523
  const detectedStackSet = new Set(detected?.stack || []);
2255
- const stackResponse = await prompts3({
2524
+ const preselected = allStackOptions.map((s, i) => detectedStackSet.has(s.value) ? i : -1).filter((i) => i !== -1);
2525
+ if (preselected.length > 0) {
2526
+ console.log(chalk8.gray(` Auto-selected: ${detected?.stack?.join(", ")}`));
2527
+ console.log();
2528
+ }
2529
+ const stackResponse = await prompts4({
2256
2530
  type: "multiselect",
2257
2531
  name: "stack",
2258
- message: "Tech stack:",
2532
+ message: chalk8.white("Tech stack:"),
2259
2533
  choices: allStackOptions.map((s) => ({
2260
2534
  title: s.title,
2261
2535
  value: s.value,
2262
2536
  selected: detectedStackSet.has(s.value)
2263
2537
  })),
2264
- hint: "- Space to select, Enter to confirm"
2265
- });
2538
+ hint: chalk8.gray("space select \u2022 a toggle all \u2022 enter confirm"),
2539
+ instructions: false
2540
+ }, promptConfig);
2266
2541
  answers.stack = stackResponse.stack || [];
2267
- const personaResponse = await prompts3({
2542
+ showStep(4, totalSteps, "AI Persona");
2543
+ const personaResponse = await prompts4({
2268
2544
  type: "select",
2269
2545
  name: "persona",
2270
- message: "AI persona:",
2271
- choices: PERSONAS,
2272
- initial: 0
2273
- // Full-stack by default
2274
- });
2546
+ message: chalk8.white("What role should the AI take?"),
2547
+ choices: PERSONAS.map((p) => ({
2548
+ title: p.title,
2549
+ value: p.value,
2550
+ description: chalk8.gray(p.description)
2551
+ })),
2552
+ initial: 0,
2553
+ hint: chalk8.gray("\u2191\u2193 navigate \u2022 enter select")
2554
+ }, promptConfig);
2275
2555
  if (personaResponse.persona === "custom") {
2276
- const customPersona = await prompts3({
2556
+ const customPersona = await prompts4({
2277
2557
  type: "text",
2278
2558
  name: "value",
2279
- message: "Describe the custom persona:"
2280
- });
2559
+ message: chalk8.white("Describe the custom persona:"),
2560
+ hint: chalk8.gray("e.g., 'ML engineer focused on PyTorch and data pipelines'")
2561
+ }, promptConfig);
2281
2562
  answers.persona = customPersona.value || "fullstack";
2282
2563
  } else {
2283
2564
  answers.persona = personaResponse.persona || "fullstack";
2284
2565
  }
2285
- const boundaryResponse = await prompts3({
2566
+ showStep(5, totalSteps, "AI Boundaries");
2567
+ const boundaryResponse = await prompts4({
2286
2568
  type: "select",
2287
2569
  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
- });
2570
+ message: chalk8.white("How much freedom should the AI have?"),
2571
+ choices: BOUNDARY_PRESETS.map((b) => ({
2572
+ title: b.title,
2573
+ value: b.value,
2574
+ description: chalk8.gray(b.description)
2575
+ })),
2576
+ initial: 0,
2577
+ hint: chalk8.gray("\u2191\u2193 navigate \u2022 enter select")
2578
+ }, promptConfig);
2293
2579
  answers.boundaries = boundaryResponse.boundaries || "standard";
2294
- if (detected?.commands && Object.keys(detected.commands).length > 0) {
2295
- console.log();
2296
- console.log(chalk7.gray("Auto-detected commands:"));
2297
- if (detected.commands.build) console.log(chalk7.gray(` Build: ${detected.commands.build}`));
2298
- if (detected.commands.test) console.log(chalk7.gray(` Test: ${detected.commands.test}`));
2299
- if (detected.commands.lint) console.log(chalk7.gray(` Lint: ${detected.commands.lint}`));
2300
- if (detected.commands.dev) console.log(chalk7.gray(` Dev: ${detected.commands.dev}`));
2301
- const editCommands = await prompts3({
2302
- type: "confirm",
2303
- name: "edit",
2304
- message: "Edit commands?",
2305
- initial: false
2306
- });
2307
- if (editCommands.edit) {
2308
- const commandsResponse = await prompts3([
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 }
2313
- ]);
2314
- answers.commands = commandsResponse;
2315
- } else {
2316
- answers.commands = detected.commands;
2317
- }
2318
- } else {
2319
- answers.commands = {};
2580
+ const selectedBoundary = BOUNDARY_PRESETS.find((b) => b.value === answers.boundaries);
2581
+ if (selectedBoundary) {
2582
+ console.log();
2583
+ console.log(chalk8.gray(" Always allowed: ") + chalk8.green(selectedBoundary.always.slice(0, 2).join(", ")));
2584
+ console.log(chalk8.gray(" Ask first: ") + chalk8.yellow(selectedBoundary.askFirst.slice(0, 2).join(", ")));
2585
+ console.log(chalk8.gray(" Never: ") + chalk8.red(selectedBoundary.never.slice(0, 2).join(", ")));
2320
2586
  }
2587
+ answers.commands = detected?.commands || {};
2321
2588
  return {
2322
2589
  name: answers.name,
2323
2590
  description: answers.description,
@@ -2330,52 +2597,52 @@ async function runInteractiveWizard(options, detected) {
2330
2597
  }
2331
2598
 
2332
2599
  // src/commands/search.ts
2333
- import chalk8 from "chalk";
2334
- import ora7 from "ora";
2600
+ import chalk9 from "chalk";
2601
+ import ora8 from "ora";
2335
2602
  async function searchCommand(query, options) {
2336
- const spinner = ora7(`Searching for "${query}"...`).start();
2603
+ const spinner = ora8(`Searching for "${query}"...`).start();
2337
2604
  try {
2338
2605
  const limit = parseInt(options.limit, 10) || 20;
2339
2606
  const { templates, total, hasMore } = await api.searchBlueprints(query, limit);
2340
2607
  spinner.stop();
2341
2608
  if (templates.length === 0) {
2342
2609
  console.log();
2343
- console.log(chalk8.yellow(`No blueprints found for "${query}".`));
2344
- console.log(chalk8.gray("Try different keywords or browse at https://lynxprompt.com/blueprints"));
2610
+ console.log(chalk9.yellow(`No blueprints found for "${query}".`));
2611
+ console.log(chalk9.gray("Try different keywords or browse at https://lynxprompt.com/blueprints"));
2345
2612
  return;
2346
2613
  }
2347
2614
  console.log();
2348
- console.log(chalk8.cyan(`\u{1F50D} Search Results for "${query}" (${total} found)`));
2615
+ console.log(chalk9.cyan(`\u{1F50D} Search Results for "${query}" (${total} found)`));
2349
2616
  console.log();
2350
2617
  for (const result of templates) {
2351
2618
  printSearchResult(result);
2352
2619
  }
2353
2620
  if (hasMore) {
2354
- console.log(chalk8.gray(`Showing ${templates.length} of ${total}. Use --limit to see more.`));
2621
+ console.log(chalk9.gray(`Showing ${templates.length} of ${total}. Use --limit to see more.`));
2355
2622
  }
2356
2623
  console.log();
2357
- console.log(chalk8.gray("Use 'lynxprompt pull <id>' to download a blueprint."));
2624
+ console.log(chalk9.gray("Use 'lynxprompt pull <id>' to download a blueprint."));
2358
2625
  } catch (error) {
2359
2626
  spinner.fail("Search failed");
2360
2627
  handleApiError3(error);
2361
2628
  }
2362
2629
  }
2363
2630
  function printSearchResult(result) {
2364
- const priceInfo = result.price ? chalk8.yellow(`\u20AC${(result.price / 100).toFixed(2)}`) : chalk8.green("Free");
2365
- const officialBadge = result.isOfficial ? chalk8.magenta(" \u2605 Official") : "";
2366
- console.log(` ${chalk8.bold(result.name)}${officialBadge}`);
2367
- console.log(` ${chalk8.cyan(result.id)} \u2022 ${priceInfo}`);
2631
+ const priceInfo = result.price ? chalk9.yellow(`\u20AC${(result.price / 100).toFixed(2)}`) : chalk9.green("Free");
2632
+ const officialBadge = result.isOfficial ? chalk9.magenta(" \u2605 Official") : "";
2633
+ console.log(` ${chalk9.bold(result.name)}${officialBadge}`);
2634
+ console.log(` ${chalk9.cyan(result.id)} \u2022 ${priceInfo}`);
2368
2635
  if (result.description) {
2369
- console.log(` ${chalk8.gray(truncate2(result.description, 60))}`);
2636
+ console.log(` ${chalk9.gray(truncate2(result.description, 60))}`);
2370
2637
  }
2371
- console.log(` ${chalk8.gray(`by ${result.author}`)} \u2022 ${chalk8.gray(`\u2193${result.downloads}`)} ${chalk8.gray(`\u2665${result.likes}`)}`);
2638
+ console.log(` ${chalk9.gray(`by ${result.author}`)} \u2022 ${chalk9.gray(`\u2193${result.downloads}`)} ${chalk9.gray(`\u2665${result.likes}`)}`);
2372
2639
  if (result.tags && result.tags.length > 0) {
2373
2640
  console.log(` ${formatTags2(result.tags)}`);
2374
2641
  }
2375
2642
  console.log();
2376
2643
  }
2377
2644
  function formatTags2(tags) {
2378
- return tags.slice(0, 4).map((t) => chalk8.gray(`#${t}`)).join(" ");
2645
+ return tags.slice(0, 4).map((t) => chalk9.gray(`#${t}`)).join(" ");
2379
2646
  }
2380
2647
  function truncate2(str, maxLength) {
2381
2648
  if (str.length <= maxLength) return str;
@@ -2383,16 +2650,16 @@ function truncate2(str, maxLength) {
2383
2650
  }
2384
2651
  function handleApiError3(error) {
2385
2652
  if (error instanceof ApiRequestError) {
2386
- console.error(chalk8.red(`Error: ${error.message}`));
2653
+ console.error(chalk9.red(`Error: ${error.message}`));
2387
2654
  } else {
2388
- console.error(chalk8.red("An unexpected error occurred."));
2655
+ console.error(chalk9.red("An unexpected error occurred."));
2389
2656
  }
2390
2657
  process.exit(1);
2391
2658
  }
2392
2659
 
2393
2660
  // src/commands/status.ts
2394
- import chalk9 from "chalk";
2395
- import { access as access6, readFile as readFile6, readdir as readdir3 } from "fs/promises";
2661
+ import chalk10 from "chalk";
2662
+ import { readFile as readFile5, readdir, access as access4 } from "fs/promises";
2396
2663
  import { join as join7 } from "path";
2397
2664
  import { existsSync as existsSync4 } from "fs";
2398
2665
  var CONFIG_FILES = [
@@ -2413,20 +2680,20 @@ var CONFIG_DIRS = [
2413
2680
  async function statusCommand() {
2414
2681
  const cwd = process.cwd();
2415
2682
  console.log();
2416
- console.log(chalk9.cyan("\u{1F431} LynxPrompt Status"));
2417
- console.log(chalk9.gray(` Directory: ${cwd}`));
2683
+ console.log(chalk10.cyan("\u{1F431} LynxPrompt Status"));
2684
+ console.log(chalk10.gray(` Directory: ${cwd}`));
2418
2685
  console.log();
2419
2686
  const lynxpromptExists = existsSync4(join7(cwd, ".lynxprompt"));
2420
2687
  if (lynxpromptExists) {
2421
- console.log(chalk9.green("\u2713 LynxPrompt initialized"));
2688
+ console.log(chalk10.green("\u2713 LynxPrompt initialized"));
2422
2689
  const configPath = join7(cwd, ".lynxprompt/conf.yml");
2423
2690
  if (existsSync4(configPath)) {
2424
2691
  try {
2425
- const content = await readFile6(configPath, "utf-8");
2692
+ const content = await readFile5(configPath, "utf-8");
2426
2693
  const { parse: parse5 } = await import("yaml");
2427
2694
  const config2 = parse5(content);
2428
2695
  if (config2?.exporters?.length > 0) {
2429
- console.log(chalk9.gray(` Exporters: ${config2.exporters.join(", ")}`));
2696
+ console.log(chalk10.gray(` Exporters: ${config2.exporters.join(", ")}`));
2430
2697
  }
2431
2698
  } catch {
2432
2699
  }
@@ -2435,49 +2702,49 @@ async function statusCommand() {
2435
2702
  }
2436
2703
  const trackedStatus = await checkSyncStatus(cwd);
2437
2704
  if (trackedStatus.length > 0) {
2438
- console.log(chalk9.cyan("\u{1F4E6} Tracked Blueprints"));
2705
+ console.log(chalk10.cyan("\u{1F4E6} Tracked Blueprints"));
2439
2706
  console.log();
2440
2707
  for (const { blueprint, localModified, fileExists: fileExists2 } of trackedStatus) {
2441
- const statusIcon = !fileExists2 ? chalk9.red("\u2717") : localModified ? chalk9.yellow("\u25CF") : chalk9.green("\u2713");
2708
+ const statusIcon = !fileExists2 ? chalk10.red("\u2717") : localModified ? chalk10.yellow("\u25CF") : chalk10.green("\u2713");
2442
2709
  const sourceLabel = {
2443
- marketplace: chalk9.gray("[marketplace]"),
2444
- team: chalk9.blue("[team]"),
2445
- private: chalk9.green("[private]"),
2446
- local: chalk9.gray("[local]")
2710
+ marketplace: chalk10.gray("[marketplace]"),
2711
+ team: chalk10.blue("[team]"),
2712
+ private: chalk10.green("[private]"),
2713
+ local: chalk10.gray("[local]")
2447
2714
  }[blueprint.source];
2448
- console.log(` ${statusIcon} ${chalk9.bold(blueprint.file)} ${sourceLabel}`);
2449
- console.log(` ${chalk9.gray(`ID: ${blueprint.id} \u2022 ${blueprint.name}`)}`);
2715
+ console.log(` ${statusIcon} ${chalk10.bold(blueprint.file)} ${sourceLabel}`);
2716
+ console.log(` ${chalk10.gray(`ID: ${blueprint.id} \u2022 ${blueprint.name}`)}`);
2450
2717
  if (!fileExists2) {
2451
- console.log(chalk9.red(` \u26A0 File missing - run 'lynxp pull ${blueprint.id}' to restore`));
2718
+ console.log(chalk10.red(` \u26A0 File missing - run 'lynxp pull ${blueprint.id}' to restore`));
2452
2719
  } else if (localModified) {
2453
2720
  if (blueprint.source === "marketplace") {
2454
- console.log(chalk9.yellow(` \u26A0 Local changes (marketplace = read-only, won't sync back)`));
2721
+ console.log(chalk10.yellow(` \u26A0 Local changes (marketplace = read-only, won't sync back)`));
2455
2722
  } else {
2456
- console.log(chalk9.yellow(` \u26A0 Local changes - run 'lynxp push ${blueprint.file}' to sync`));
2723
+ console.log(chalk10.yellow(` \u26A0 Local changes - run 'lynxp push ${blueprint.file}' to sync`));
2457
2724
  }
2458
2725
  }
2459
2726
  console.log();
2460
2727
  }
2461
2728
  }
2462
- console.log(chalk9.cyan("\u{1F4C4} AI Config Files"));
2729
+ console.log(chalk10.cyan("\u{1F4C4} AI Config Files"));
2463
2730
  console.log();
2464
2731
  let foundAny = false;
2465
2732
  for (const config2 of CONFIG_FILES) {
2466
2733
  const filePath = join7(cwd, config2.path);
2467
2734
  try {
2468
- await access6(filePath);
2469
- const content = await readFile6(filePath, "utf-8");
2735
+ await access4(filePath);
2736
+ const content = await readFile5(filePath, "utf-8");
2470
2737
  const lines = content.split("\n").length;
2471
2738
  const size = formatBytes(content.length);
2472
2739
  foundAny = true;
2473
2740
  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}`);
2476
- console.log(` ${chalk9.gray(`Platform: ${config2.platform}`)}`);
2477
- console.log(` ${chalk9.gray(`Size: ${size} (${lines} lines)`)}`);
2741
+ const trackedLabel = tracked ? chalk10.cyan(" (tracked)") : "";
2742
+ console.log(` ${chalk10.green("\u2713")} ${chalk10.bold(config2.name)}${trackedLabel}`);
2743
+ console.log(` ${chalk10.gray(`Platform: ${config2.platform}`)}`);
2744
+ console.log(` ${chalk10.gray(`Size: ${size} (${lines} lines)`)}`);
2478
2745
  const preview = getPreview(content);
2479
2746
  if (preview) {
2480
- console.log(` ${chalk9.gray(`Preview: ${preview}`)}`);
2747
+ console.log(` ${chalk10.gray(`Preview: ${preview}`)}`);
2481
2748
  }
2482
2749
  console.log();
2483
2750
  } catch {
@@ -2487,18 +2754,18 @@ async function statusCommand() {
2487
2754
  const dirPath = join7(cwd, config2.path);
2488
2755
  if (existsSync4(dirPath)) {
2489
2756
  try {
2490
- const files = await readdir3(dirPath);
2757
+ const files = await readdir(dirPath);
2491
2758
  const ruleFiles = files.filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
2492
2759
  if (ruleFiles.length > 0) {
2493
2760
  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"}`)}`);
2761
+ console.log(` ${chalk10.green("\u2713")} ${chalk10.bold(config2.name)}`);
2762
+ console.log(` ${chalk10.gray(`Platform: ${config2.platform}`)}`);
2763
+ console.log(` ${chalk10.gray(`Rules: ${ruleFiles.length} file${ruleFiles.length === 1 ? "" : "s"}`)}`);
2497
2764
  for (const file of ruleFiles.slice(0, 3)) {
2498
- console.log(` ${chalk9.gray(` \u2022 ${file}`)}`);
2765
+ console.log(` ${chalk10.gray(` \u2022 ${file}`)}`);
2499
2766
  }
2500
2767
  if (ruleFiles.length > 3) {
2501
- console.log(` ${chalk9.gray(` ... and ${ruleFiles.length - 3} more`)}`);
2768
+ console.log(` ${chalk10.gray(` ... and ${ruleFiles.length - 3} more`)}`);
2502
2769
  }
2503
2770
  console.log();
2504
2771
  }
@@ -2507,17 +2774,17 @@ async function statusCommand() {
2507
2774
  }
2508
2775
  }
2509
2776
  if (!foundAny) {
2510
- console.log(chalk9.yellow(" No AI configuration files found."));
2777
+ console.log(chalk10.yellow(" No AI configuration files found."));
2511
2778
  console.log();
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"));
2779
+ console.log(chalk10.gray(" Get started:"));
2780
+ console.log(chalk10.gray(" lynxp wizard Generate a configuration"));
2781
+ console.log(chalk10.gray(" lynxp pull <id> Download from marketplace"));
2782
+ console.log(chalk10.gray(" lynxp search <query> Search for blueprints"));
2516
2783
  } else {
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"));
2784
+ console.log(chalk10.gray("Commands:"));
2785
+ console.log(chalk10.gray(" lynxp wizard Regenerate configuration"));
2786
+ console.log(chalk10.gray(" lynxp check Validate files"));
2787
+ console.log(chalk10.gray(" lynxp link --list Show tracked blueprints"));
2521
2788
  }
2522
2789
  console.log();
2523
2790
  }
@@ -2542,10 +2809,10 @@ function formatBytes(bytes) {
2542
2809
  }
2543
2810
 
2544
2811
  // src/commands/sync.ts
2545
- import chalk10 from "chalk";
2546
- import ora8 from "ora";
2547
- import prompts4 from "prompts";
2548
- import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5, readdir as readdir4 } from "fs/promises";
2812
+ import chalk11 from "chalk";
2813
+ import ora9 from "ora";
2814
+ import prompts5 from "prompts";
2815
+ import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir5, readdir as readdir2 } from "fs/promises";
2549
2816
  import { join as join8, dirname as dirname5 } from "path";
2550
2817
  import { existsSync as existsSync5 } from "fs";
2551
2818
  import * as yaml3 from "yaml";
@@ -2553,31 +2820,31 @@ var CONFIG_FILE = ".lynxprompt/conf.yml";
2553
2820
  var RULES_DIR = ".lynxprompt/rules";
2554
2821
  async function syncCommand(options = {}) {
2555
2822
  console.log();
2556
- console.log(chalk10.cyan("\u{1F431} LynxPrompt Sync"));
2823
+ console.log(chalk11.cyan("\u{1F431} LynxPrompt Sync"));
2557
2824
  console.log();
2558
2825
  const cwd = process.cwd();
2559
2826
  const configPath = join8(cwd, CONFIG_FILE);
2560
2827
  if (!existsSync5(configPath)) {
2561
- console.log(chalk10.yellow("LynxPrompt is not initialized in this project."));
2828
+ console.log(chalk11.yellow("LynxPrompt is not initialized in this project."));
2562
2829
  console.log();
2563
- console.log(chalk10.gray("Run 'lynxp init' first to set up LynxPrompt."));
2830
+ console.log(chalk11.gray("Run 'lynxp init' first to set up LynxPrompt."));
2564
2831
  return;
2565
2832
  }
2566
- const spinner = ora8("Loading configuration...").start();
2833
+ const spinner = ora9("Loading configuration...").start();
2567
2834
  let config2;
2568
2835
  try {
2569
- const configContent = await readFile7(configPath, "utf-8");
2836
+ const configContent = await readFile6(configPath, "utf-8");
2570
2837
  config2 = yaml3.parse(configContent);
2571
2838
  spinner.succeed("Configuration loaded");
2572
2839
  } catch (error) {
2573
2840
  spinner.fail("Failed to load configuration");
2574
- console.log(chalk10.red("Could not parse .lynxprompt/conf.yml"));
2841
+ console.log(chalk11.red("Could not parse .lynxprompt/conf.yml"));
2575
2842
  return;
2576
2843
  }
2577
2844
  if (!config2.exporters || config2.exporters.length === 0) {
2578
- console.log(chalk10.yellow("No exporters configured."));
2845
+ console.log(chalk11.yellow("No exporters configured."));
2579
2846
  console.log();
2580
- console.log(chalk10.gray("Add exporters to .lynxprompt/conf.yml or run 'lynxp agents enable <agent>'"));
2847
+ console.log(chalk11.gray("Add exporters to .lynxprompt/conf.yml or run 'lynxp agents enable <agent>'"));
2581
2848
  return;
2582
2849
  }
2583
2850
  const validExporters = [];
@@ -2591,51 +2858,51 @@ async function syncCommand(options = {}) {
2591
2858
  }
2592
2859
  }
2593
2860
  if (invalidExporters.length > 0) {
2594
- console.log(chalk10.yellow(`Unknown exporters: ${invalidExporters.join(", ")}`));
2861
+ console.log(chalk11.yellow(`Unknown exporters: ${invalidExporters.join(", ")}`));
2595
2862
  }
2596
2863
  if (validExporters.length === 0) {
2597
- console.log(chalk10.red("No valid exporters configured."));
2864
+ console.log(chalk11.red("No valid exporters configured."));
2598
2865
  return;
2599
2866
  }
2600
- console.log(chalk10.gray(`Exporters: ${validExporters.map((e) => e.name).join(", ")}`));
2867
+ console.log(chalk11.gray(`Exporters: ${validExporters.map((e) => e.name).join(", ")}`));
2601
2868
  console.log();
2602
2869
  const rulesPath = join8(cwd, RULES_DIR);
2603
2870
  if (!existsSync5(rulesPath)) {
2604
- console.log(chalk10.yellow("No rules found."));
2605
- console.log(chalk10.gray(`Create rules in ${RULES_DIR}/ to sync them.`));
2871
+ console.log(chalk11.yellow("No rules found."));
2872
+ console.log(chalk11.gray(`Create rules in ${RULES_DIR}/ to sync them.`));
2606
2873
  return;
2607
2874
  }
2608
2875
  const rulesContent = await loadRules(rulesPath);
2609
2876
  if (!rulesContent) {
2610
- console.log(chalk10.yellow("No rule files found in .lynxprompt/rules/"));
2877
+ console.log(chalk11.yellow("No rule files found in .lynxprompt/rules/"));
2611
2878
  return;
2612
2879
  }
2613
- console.log(chalk10.gray(`Loaded ${rulesContent.fileCount} rule file${rulesContent.fileCount === 1 ? "" : "s"}`));
2880
+ console.log(chalk11.gray(`Loaded ${rulesContent.fileCount} rule file${rulesContent.fileCount === 1 ? "" : "s"}`));
2614
2881
  console.log();
2615
2882
  if (options.dryRun) {
2616
- console.log(chalk10.cyan("Dry run - no files will be written"));
2883
+ console.log(chalk11.cyan("Dry run - no files will be written"));
2617
2884
  console.log();
2618
2885
  console.log("Would write:");
2619
2886
  for (const exporter of validExporters) {
2620
- console.log(chalk10.gray(` ${exporter.output}`));
2887
+ console.log(chalk11.gray(` ${exporter.output}`));
2621
2888
  }
2622
2889
  console.log();
2623
2890
  return;
2624
2891
  }
2625
2892
  if (!options.force) {
2626
- const { confirm } = await prompts4({
2893
+ const { confirm } = await prompts5({
2627
2894
  type: "confirm",
2628
2895
  name: "confirm",
2629
2896
  message: `Sync to ${validExporters.length} agent${validExporters.length === 1 ? "" : "s"}?`,
2630
2897
  initial: true
2631
2898
  });
2632
2899
  if (!confirm) {
2633
- console.log(chalk10.gray("Cancelled."));
2900
+ console.log(chalk11.gray("Cancelled."));
2634
2901
  return;
2635
2902
  }
2636
2903
  }
2637
2904
  const result = { written: [], skipped: [], errors: [] };
2638
- const syncSpinner = ora8("Syncing rules...").start();
2905
+ const syncSpinner = ora9("Syncing rules...").start();
2639
2906
  for (const exporter of validExporters) {
2640
2907
  try {
2641
2908
  await syncToAgent(cwd, exporter, rulesContent.combined);
@@ -2646,16 +2913,16 @@ async function syncCommand(options = {}) {
2646
2913
  }
2647
2914
  syncSpinner.stop();
2648
2915
  if (result.written.length > 0) {
2649
- console.log(chalk10.green(`\u2713 Synced to ${result.written.length} agent${result.written.length === 1 ? "" : "s"}:`));
2916
+ console.log(chalk11.green(`\u2713 Synced to ${result.written.length} agent${result.written.length === 1 ? "" : "s"}:`));
2650
2917
  for (const file of result.written) {
2651
- console.log(chalk10.gray(` ${file}`));
2918
+ console.log(chalk11.gray(` ${file}`));
2652
2919
  }
2653
2920
  }
2654
2921
  if (result.errors.length > 0) {
2655
2922
  console.log();
2656
- console.log(chalk10.red("Errors:"));
2923
+ console.log(chalk11.red("Errors:"));
2657
2924
  for (const error of result.errors) {
2658
- console.log(chalk10.red(` ${error}`));
2925
+ console.log(chalk11.red(` ${error}`));
2659
2926
  }
2660
2927
  }
2661
2928
  console.log();
@@ -2663,12 +2930,12 @@ async function syncCommand(options = {}) {
2663
2930
  async function loadRules(rulesPath) {
2664
2931
  const files = [];
2665
2932
  try {
2666
- const entries = await readdir4(rulesPath, { withFileTypes: true });
2933
+ const entries = await readdir2(rulesPath, { withFileTypes: true });
2667
2934
  for (const entry of entries) {
2668
2935
  if (!entry.isFile()) continue;
2669
2936
  if (!entry.name.endsWith(".md")) continue;
2670
2937
  const filePath = join8(rulesPath, entry.name);
2671
- const content = await readFile7(filePath, "utf-8");
2938
+ const content = await readFile6(filePath, "utf-8");
2672
2939
  if (content.trim()) {
2673
2940
  files.push({ name: entry.name, content: content.trim() });
2674
2941
  }
@@ -2718,7 +2985,7 @@ function formatForAgent(agent, content) {
2718
2985
  return content;
2719
2986
  }
2720
2987
  }
2721
- function formatAsMdc(content, agent) {
2988
+ function formatAsMdc(content, _agent) {
2722
2989
  const frontmatter = yaml3.stringify({
2723
2990
  description: "LynxPrompt rules - AI coding guidelines",
2724
2991
  globs: ["**/*"],
@@ -2729,7 +2996,7 @@ ${frontmatter}---
2729
2996
 
2730
2997
  ${content}`;
2731
2998
  }
2732
- function formatAsMarkdown(content, agent) {
2999
+ function formatAsMarkdown(content, _agent) {
2733
3000
  const header = `# AI Coding Rules
2734
3001
 
2735
3002
  > Generated by [LynxPrompt](https://lynxprompt.com)
@@ -2737,7 +3004,7 @@ function formatAsMarkdown(content, agent) {
2737
3004
  `;
2738
3005
  return header + content;
2739
3006
  }
2740
- function formatAsJson(content, agent) {
3007
+ function formatAsJson(content, _agent) {
2741
3008
  return JSON.stringify(
2742
3009
  {
2743
3010
  $schema: "https://lynxprompt.com/schemas/rules.json",
@@ -2754,9 +3021,9 @@ function formatAsJson(content, agent) {
2754
3021
  }
2755
3022
 
2756
3023
  // src/commands/agents.ts
2757
- import chalk11 from "chalk";
2758
- import prompts5 from "prompts";
2759
- import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
3024
+ import chalk12 from "chalk";
3025
+ import prompts6 from "prompts";
3026
+ import { readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
2760
3027
  import { join as join9 } from "path";
2761
3028
  import { existsSync as existsSync6 } from "fs";
2762
3029
  import * as yaml4 from "yaml";
@@ -2780,18 +3047,18 @@ async function agentsCommand(action, agentId, options = {}) {
2780
3047
  }
2781
3048
  }
2782
3049
  async function listAgents() {
2783
- console.log(chalk11.cyan("\u{1F431} LynxPrompt Agents"));
3050
+ console.log(chalk12.cyan("\u{1F431} LynxPrompt Agents"));
2784
3051
  console.log();
2785
3052
  const config2 = await loadConfig();
2786
3053
  const enabledSet = new Set(config2?.exporters ?? []);
2787
3054
  const detection = detectAgents();
2788
3055
  if (enabledSet.size > 0) {
2789
- console.log(chalk11.green("Enabled:"));
3056
+ console.log(chalk12.green("Enabled:"));
2790
3057
  for (const id of enabledSet) {
2791
3058
  const agent = getAgent(id);
2792
3059
  const detected = detection.detected.find((d) => d.agent.id === id);
2793
- const status = detected ? chalk11.gray("(detected)") : "";
2794
- console.log(` ${chalk11.green("\u2713")} ${agent?.name ?? id} ${status}`);
3060
+ const status = detected ? chalk12.gray("(detected)") : "";
3061
+ console.log(` ${chalk12.green("\u2713")} ${agent?.name ?? id} ${status}`);
2795
3062
  }
2796
3063
  console.log();
2797
3064
  }
@@ -2799,10 +3066,10 @@ async function listAgents() {
2799
3066
  (d) => !enabledSet.has(d.agent.id)
2800
3067
  );
2801
3068
  if (detectedNotEnabled.length > 0) {
2802
- console.log(chalk11.yellow("Detected (not enabled):"));
3069
+ console.log(chalk12.yellow("Detected (not enabled):"));
2803
3070
  for (const detected of detectedNotEnabled) {
2804
- const rules = detected.ruleCount > 0 ? chalk11.gray(` (${detected.ruleCount} rules)`) : "";
2805
- console.log(` ${chalk11.yellow("\u25CB")} ${detected.agent.name}${rules}`);
3071
+ const rules = detected.ruleCount > 0 ? chalk12.gray(` (${detected.ruleCount} rules)`) : "";
3072
+ console.log(` ${chalk12.yellow("\u25CB")} ${detected.agent.name}${rules}`);
2806
3073
  }
2807
3074
  console.log();
2808
3075
  }
@@ -2810,30 +3077,30 @@ async function listAgents() {
2810
3077
  (a) => !enabledSet.has(a.id) && !detectedNotEnabled.some((d) => d.agent.id === a.id)
2811
3078
  );
2812
3079
  if (popular.length > 0) {
2813
- console.log(chalk11.gray("Popular (available):"));
3080
+ console.log(chalk12.gray("Popular (available):"));
2814
3081
  for (const agent of popular) {
2815
- console.log(` ${chalk11.gray("-")} ${agent.name} - ${agent.description}`);
3082
+ console.log(` ${chalk12.gray("-")} ${agent.name} - ${agent.description}`);
2816
3083
  }
2817
3084
  console.log();
2818
3085
  }
2819
- console.log(chalk11.gray(`Total: ${AGENTS.length} agents supported`));
3086
+ console.log(chalk12.gray(`Total: ${AGENTS.length} agents supported`));
2820
3087
  console.log();
2821
- console.log(chalk11.gray("Commands:"));
2822
- console.log(chalk11.gray(" lynxp agents enable <agent> Enable an agent"));
2823
- console.log(chalk11.gray(" lynxp agents disable <agent> Disable an agent"));
2824
- console.log(chalk11.gray(" lynxp agents detect Auto-detect agents"));
3088
+ console.log(chalk12.gray("Commands:"));
3089
+ console.log(chalk12.gray(" lynxp agents enable <agent> Enable an agent"));
3090
+ console.log(chalk12.gray(" lynxp agents disable <agent> Disable an agent"));
3091
+ console.log(chalk12.gray(" lynxp agents detect Auto-detect agents"));
2825
3092
  console.log();
2826
3093
  }
2827
3094
  async function enableAgent(agentId, options = {}) {
2828
3095
  const cwd = process.cwd();
2829
3096
  const configPath = join9(cwd, CONFIG_FILE2);
2830
3097
  if (!existsSync6(configPath)) {
2831
- console.log(chalk11.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
3098
+ console.log(chalk12.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
2832
3099
  return;
2833
3100
  }
2834
3101
  let config2 = await loadConfig();
2835
3102
  if (!config2) {
2836
- console.log(chalk11.red("Could not load configuration."));
3103
+ console.log(chalk12.red("Could not load configuration."));
2837
3104
  return;
2838
3105
  }
2839
3106
  if (!agentId || options.interactive) {
@@ -2843,7 +3110,7 @@ async function enableAgent(agentId, options = {}) {
2843
3110
  value: agent2.id,
2844
3111
  selected: enabledSet.has(agent2.id)
2845
3112
  }));
2846
- const { selected } = await prompts5({
3113
+ const { selected } = await prompts6({
2847
3114
  type: "multiselect",
2848
3115
  name: "selected",
2849
3116
  message: "Select agents to enable:",
@@ -2851,14 +3118,14 @@ async function enableAgent(agentId, options = {}) {
2851
3118
  hint: "- Space to select, Enter to confirm"
2852
3119
  });
2853
3120
  if (!selected || selected.length === 0) {
2854
- console.log(chalk11.yellow("No agents selected."));
3121
+ console.log(chalk12.yellow("No agents selected."));
2855
3122
  return;
2856
3123
  }
2857
3124
  config2.exporters = selected;
2858
3125
  await saveConfig(config2);
2859
- console.log(chalk11.green(`\u2713 Enabled ${selected.length} agent${selected.length === 1 ? "" : "s"}`));
3126
+ console.log(chalk12.green(`\u2713 Enabled ${selected.length} agent${selected.length === 1 ? "" : "s"}`));
2860
3127
  console.log();
2861
- console.log(chalk11.gray("Run 'lynxp sync' to sync your rules."));
3128
+ console.log(chalk12.gray("Run 'lynxp sync' to sync your rules."));
2862
3129
  return;
2863
3130
  }
2864
3131
  const agent = getAgent(agentId);
@@ -2866,12 +3133,12 @@ async function enableAgent(agentId, options = {}) {
2866
3133
  const similar = AGENTS.filter(
2867
3134
  (a) => a.id.includes(agentId.toLowerCase()) || a.name.toLowerCase().includes(agentId.toLowerCase())
2868
3135
  );
2869
- console.log(chalk11.red(`Unknown agent: ${agentId}`));
3136
+ console.log(chalk12.red(`Unknown agent: ${agentId}`));
2870
3137
  if (similar.length > 0) {
2871
3138
  console.log();
2872
- console.log(chalk11.gray("Did you mean:"));
3139
+ console.log(chalk12.gray("Did you mean:"));
2873
3140
  for (const a of similar.slice(0, 5)) {
2874
- console.log(chalk11.gray(` ${a.id} - ${a.name}`));
3141
+ console.log(chalk12.gray(` ${a.id} - ${a.name}`));
2875
3142
  }
2876
3143
  }
2877
3144
  return;
@@ -2880,48 +3147,48 @@ async function enableAgent(agentId, options = {}) {
2880
3147
  config2.exporters = [];
2881
3148
  }
2882
3149
  if (config2.exporters.includes(agent.id)) {
2883
- console.log(chalk11.yellow(`${agent.name} is already enabled.`));
3150
+ console.log(chalk12.yellow(`${agent.name} is already enabled.`));
2884
3151
  return;
2885
3152
  }
2886
3153
  config2.exporters.push(agent.id);
2887
3154
  await saveConfig(config2);
2888
- console.log(chalk11.green(`\u2713 Enabled ${agent.name}`));
3155
+ console.log(chalk12.green(`\u2713 Enabled ${agent.name}`));
2889
3156
  console.log();
2890
- console.log(chalk11.gray(`Output: ${agent.output}`));
2891
- console.log(chalk11.gray("Run 'lynxp sync' to sync your rules."));
3157
+ console.log(chalk12.gray(`Output: ${agent.output}`));
3158
+ console.log(chalk12.gray("Run 'lynxp sync' to sync your rules."));
2892
3159
  }
2893
3160
  async function disableAgent(agentId) {
2894
3161
  if (!agentId) {
2895
- console.log(chalk11.yellow("Usage: lynxp agents disable <agent>"));
3162
+ console.log(chalk12.yellow("Usage: lynxp agents disable <agent>"));
2896
3163
  return;
2897
3164
  }
2898
3165
  const cwd = process.cwd();
2899
3166
  const configPath = join9(cwd, CONFIG_FILE2);
2900
3167
  if (!existsSync6(configPath)) {
2901
- console.log(chalk11.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
3168
+ console.log(chalk12.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
2902
3169
  return;
2903
3170
  }
2904
3171
  const config2 = await loadConfig();
2905
3172
  if (!config2) {
2906
- console.log(chalk11.red("Could not load configuration."));
3173
+ console.log(chalk12.red("Could not load configuration."));
2907
3174
  return;
2908
3175
  }
2909
3176
  if (!config2.exporters || !config2.exporters.includes(agentId)) {
2910
3177
  const agent2 = getAgent(agentId);
2911
- console.log(chalk11.yellow(`${agent2?.name ?? agentId} is not enabled.`));
3178
+ console.log(chalk12.yellow(`${agent2?.name ?? agentId} is not enabled.`));
2912
3179
  return;
2913
3180
  }
2914
3181
  if (config2.exporters.length === 1) {
2915
- console.log(chalk11.yellow("Cannot disable the last agent. At least one must be enabled."));
3182
+ console.log(chalk12.yellow("Cannot disable the last agent. At least one must be enabled."));
2916
3183
  return;
2917
3184
  }
2918
3185
  config2.exporters = config2.exporters.filter((e) => e !== agentId);
2919
3186
  await saveConfig(config2);
2920
3187
  const agent = getAgent(agentId);
2921
- console.log(chalk11.green(`\u2713 Disabled ${agent?.name ?? agentId}`));
3188
+ console.log(chalk12.green(`\u2713 Disabled ${agent?.name ?? agentId}`));
2922
3189
  }
2923
3190
  async function detectAgentsInProject() {
2924
- console.log(chalk11.cyan("\u{1F50D} Detecting AI agents..."));
3191
+ console.log(chalk12.cyan("\u{1F50D} Detecting AI agents..."));
2925
3192
  console.log();
2926
3193
  const detection = detectAgents();
2927
3194
  console.log(formatDetectionResults(detection));
@@ -2933,10 +3200,10 @@ async function detectAgentsInProject() {
2933
3200
  const enabledSet = new Set(config2?.exporters ?? []);
2934
3201
  const newAgents = detection.detected.filter((d) => !enabledSet.has(d.agent.id));
2935
3202
  if (newAgents.length === 0) {
2936
- console.log(chalk11.gray("All detected agents are already enabled."));
3203
+ console.log(chalk12.gray("All detected agents are already enabled."));
2937
3204
  return;
2938
3205
  }
2939
- const { enable } = await prompts5({
3206
+ const { enable } = await prompts6({
2940
3207
  type: "confirm",
2941
3208
  name: "enable",
2942
3209
  message: `Enable ${newAgents.length} detected agent${newAgents.length === 1 ? "" : "s"}?`,
@@ -2948,7 +3215,7 @@ async function detectAgentsInProject() {
2948
3215
  ...newAgents.map((d) => d.agent.id)
2949
3216
  ];
2950
3217
  await saveConfig(config2);
2951
- console.log(chalk11.green(`\u2713 Enabled ${newAgents.length} agent${newAgents.length === 1 ? "" : "s"}`));
3218
+ console.log(chalk12.green(`\u2713 Enabled ${newAgents.length} agent${newAgents.length === 1 ? "" : "s"}`));
2952
3219
  }
2953
3220
  }
2954
3221
  async function loadConfig() {
@@ -2958,7 +3225,7 @@ async function loadConfig() {
2958
3225
  return null;
2959
3226
  }
2960
3227
  try {
2961
- const content = await readFile8(configPath, "utf-8");
3228
+ const content = await readFile7(configPath, "utf-8");
2962
3229
  return yaml4.parse(content);
2963
3230
  } catch {
2964
3231
  return null;
@@ -2972,9 +3239,9 @@ async function saveConfig(config2) {
2972
3239
  }
2973
3240
 
2974
3241
  // 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";
3242
+ import chalk13 from "chalk";
3243
+ import ora10 from "ora";
3244
+ import { readFile as readFile8, readdir as readdir3, stat } from "fs/promises";
2978
3245
  import { join as join10 } from "path";
2979
3246
  import { existsSync as existsSync7 } from "fs";
2980
3247
  import * as yaml5 from "yaml";
@@ -3040,7 +3307,7 @@ async function validateLynxPromptConfig(cwd) {
3040
3307
  return { errors, warnings };
3041
3308
  }
3042
3309
  try {
3043
- const content = await readFile9(configPath, "utf-8");
3310
+ const content = await readFile8(configPath, "utf-8");
3044
3311
  const config2 = yaml5.parse(content);
3045
3312
  if (!config2.version) {
3046
3313
  warnings.push(".lynxprompt/conf.yml: Missing 'version' field");
@@ -3098,7 +3365,7 @@ async function checkCommand(options = {}) {
3098
3365
  const cwd = process.cwd();
3099
3366
  if (!isCi) {
3100
3367
  console.log();
3101
- console.log(chalk12.cyan("\u{1F431} LynxPrompt Check"));
3368
+ console.log(chalk13.cyan("\u{1F431} LynxPrompt Check"));
3102
3369
  console.log();
3103
3370
  }
3104
3371
  const result = {
@@ -3107,13 +3374,13 @@ async function checkCommand(options = {}) {
3107
3374
  warnings: [],
3108
3375
  files: []
3109
3376
  };
3110
- const spinner = !isCi ? ora9("Scanning for configuration files...").start() : null;
3377
+ const spinner = !isCi ? ora10("Scanning for configuration files...").start() : null;
3111
3378
  for (const file of CONFIG_FILES2) {
3112
3379
  const filePath = join10(cwd, file.path);
3113
3380
  if (existsSync7(filePath)) {
3114
3381
  result.files.push(file.path);
3115
3382
  try {
3116
- const content = await readFile9(filePath, "utf-8");
3383
+ const content = await readFile8(filePath, "utf-8");
3117
3384
  const validation = validateMarkdown(content, file.path);
3118
3385
  result.errors.push(...validation.errors);
3119
3386
  result.warnings.push(...validation.warnings);
@@ -3126,13 +3393,13 @@ async function checkCommand(options = {}) {
3126
3393
  const dirPath = join10(cwd, dir.path);
3127
3394
  if (existsSync7(dirPath)) {
3128
3395
  try {
3129
- const files = await readdir5(dirPath);
3396
+ const files = await readdir3(dirPath);
3130
3397
  for (const file of files) {
3131
3398
  const filePath = join10(dirPath, file);
3132
- const fileStat = await stat2(filePath);
3399
+ const fileStat = await stat(filePath);
3133
3400
  if (fileStat.isFile()) {
3134
3401
  result.files.push(`${dir.path}/${file}`);
3135
- const content = await readFile9(filePath, "utf-8");
3402
+ const content = await readFile8(filePath, "utf-8");
3136
3403
  if (file.endsWith(".mdc")) {
3137
3404
  const validation = validateMdc(content, `${dir.path}/${file}`);
3138
3405
  result.errors.push(...validation.errors);
@@ -3172,43 +3439,43 @@ async function checkCommand(options = {}) {
3172
3439
  }
3173
3440
  } else {
3174
3441
  if (result.files.length === 0) {
3175
- console.log(chalk12.yellow("\u26A0 No AI configuration files found."));
3442
+ console.log(chalk13.yellow("\u26A0 No AI configuration files found."));
3176
3443
  console.log();
3177
- console.log(chalk12.gray("Run 'lynxp wizard' to create a configuration."));
3444
+ console.log(chalk13.gray("Run 'lynxp wizard' to create a configuration."));
3178
3445
  return;
3179
3446
  }
3180
- console.log(chalk12.green(`\u2713 Found ${result.files.length} configuration file${result.files.length === 1 ? "" : "s"}:`));
3447
+ console.log(chalk13.green(`\u2713 Found ${result.files.length} configuration file${result.files.length === 1 ? "" : "s"}:`));
3181
3448
  for (const file of result.files) {
3182
- console.log(chalk12.gray(` ${file}`));
3449
+ console.log(chalk13.gray(` ${file}`));
3183
3450
  }
3184
3451
  console.log();
3185
3452
  if (result.errors.length > 0) {
3186
- console.log(chalk12.red(`\u2717 ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}:`));
3453
+ console.log(chalk13.red(`\u2717 ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}:`));
3187
3454
  for (const error of result.errors) {
3188
- console.log(chalk12.red(` ${error}`));
3455
+ console.log(chalk13.red(` ${error}`));
3189
3456
  }
3190
3457
  console.log();
3191
3458
  }
3192
3459
  if (result.warnings.length > 0) {
3193
- console.log(chalk12.yellow(`\u26A0 ${result.warnings.length} warning${result.warnings.length === 1 ? "" : "s"}:`));
3460
+ console.log(chalk13.yellow(`\u26A0 ${result.warnings.length} warning${result.warnings.length === 1 ? "" : "s"}:`));
3194
3461
  for (const warning of result.warnings) {
3195
- console.log(chalk12.yellow(` ${warning}`));
3462
+ console.log(chalk13.yellow(` ${warning}`));
3196
3463
  }
3197
3464
  console.log();
3198
3465
  }
3199
3466
  if (result.valid) {
3200
- console.log(chalk12.green("\u2705 Validation passed!"));
3467
+ console.log(chalk13.green("\u2705 Validation passed!"));
3201
3468
  } else {
3202
- console.log(chalk12.red("\u274C Validation failed. Fix the errors above."));
3469
+ console.log(chalk13.red("\u274C Validation failed. Fix the errors above."));
3203
3470
  }
3204
3471
  console.log();
3205
3472
  }
3206
3473
  }
3207
3474
 
3208
3475
  // src/commands/diff.ts
3209
- import chalk13 from "chalk";
3210
- import ora10 from "ora";
3211
- import { readFile as readFile10 } from "fs/promises";
3476
+ import chalk14 from "chalk";
3477
+ import ora11 from "ora";
3478
+ import { readFile as readFile9 } from "fs/promises";
3212
3479
  import { join as join11 } from "path";
3213
3480
  import { existsSync as existsSync8 } from "fs";
3214
3481
  function computeDiff(oldText, newText) {
@@ -3272,24 +3539,23 @@ function longestCommonSubsequence(a, b) {
3272
3539
  function formatDiff(diff, contextLines = 3) {
3273
3540
  const output = [];
3274
3541
  let lastPrintedIndex = -1;
3275
- let inHunk = false;
3276
3542
  const changeIndices = diff.map((d, i) => d.type !== "same" ? i : -1).filter((i) => i !== -1);
3277
3543
  if (changeIndices.length === 0) {
3278
- return chalk13.gray(" (no changes)");
3544
+ return chalk14.gray(" (no changes)");
3279
3545
  }
3280
3546
  for (let i = 0; i < diff.length; i++) {
3281
3547
  const item = diff[i];
3282
3548
  const nearChange = changeIndices.some((ci) => Math.abs(ci - i) <= contextLines);
3283
3549
  if (nearChange) {
3284
3550
  if (lastPrintedIndex !== -1 && i - lastPrintedIndex > 1) {
3285
- output.push(chalk13.gray(" ..."));
3551
+ output.push(chalk14.gray(" ..."));
3286
3552
  }
3287
3553
  if (item.type === "add") {
3288
- output.push(chalk13.green(`+ ${item.line}`));
3554
+ output.push(chalk14.green(`+ ${item.line}`));
3289
3555
  } else if (item.type === "remove") {
3290
- output.push(chalk13.red(`- ${item.line}`));
3556
+ output.push(chalk14.red(`- ${item.line}`));
3291
3557
  } else {
3292
- output.push(chalk13.gray(` ${item.line}`));
3558
+ output.push(chalk14.gray(` ${item.line}`));
3293
3559
  }
3294
3560
  lastPrintedIndex = i;
3295
3561
  }
@@ -3303,39 +3569,115 @@ function getDiffStats(diff) {
3303
3569
  unchanged: diff.filter((d) => d.type === "same").length
3304
3570
  };
3305
3571
  }
3306
- async function diffCommand(blueprintId, options = {}) {
3572
+ async function diffCommand(fileOrId, options = {}) {
3307
3573
  console.log();
3308
- console.log(chalk13.cyan("\u{1F431} LynxPrompt Diff"));
3574
+ console.log(chalk14.cyan("\u{1F431} LynxPrompt Diff"));
3309
3575
  console.log();
3310
3576
  const cwd = process.cwd();
3311
3577
  if (options.local) {
3312
3578
  await diffLocal(cwd);
3313
3579
  return;
3314
3580
  }
3315
- if (!blueprintId) {
3316
- console.log(chalk13.red("\u2717 Please provide a blueprint ID to compare with."));
3581
+ const trackedFiles = await checkSyncStatus(cwd);
3582
+ if (fileOrId) {
3583
+ const tracked = await findBlueprintByFile(cwd, fileOrId);
3584
+ if (tracked) {
3585
+ await diffFileWithBlueprint(cwd, fileOrId, tracked.id);
3586
+ return;
3587
+ }
3588
+ await diffWithBlueprintId(cwd, fileOrId);
3589
+ return;
3590
+ }
3591
+ if (trackedFiles.length === 0) {
3592
+ console.log(chalk14.yellow("No tracked blueprints found."));
3317
3593
  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"));
3594
+ console.log(chalk14.gray("To track a blueprint and compare changes:"));
3595
+ console.log(chalk14.gray(" 1. Pull a blueprint: lynxp pull <blueprint-id>"));
3596
+ console.log(chalk14.gray(" 2. Or link an existing file: lynxp link"));
3597
+ console.log();
3598
+ console.log(chalk14.gray("Other options:"));
3599
+ console.log(chalk14.gray(" lynxp diff --local Compare .lynxprompt/rules/ with exported files"));
3600
+ return;
3601
+ }
3602
+ let hasChanges = false;
3603
+ for (const { blueprint, localModified, fileExists: fileExists2 } of trackedFiles) {
3604
+ if (!fileExists2) {
3605
+ console.log(chalk14.red(`\u2717 ${blueprint.file} - file not found`));
3606
+ continue;
3607
+ }
3608
+ console.log(chalk14.cyan(`\u{1F4C4} ${blueprint.file}`));
3609
+ console.log(chalk14.gray(` Linked to: ${blueprint.name} (${blueprint.id})`));
3610
+ if (localModified) {
3611
+ hasChanges = true;
3612
+ await diffFileWithBlueprint(cwd, blueprint.file, blueprint.id, true);
3613
+ } else {
3614
+ console.log(chalk14.green(" \u2713 In sync with cloud"));
3615
+ }
3616
+ console.log();
3617
+ }
3618
+ if (!hasChanges) {
3619
+ console.log(chalk14.green("\u2713 All tracked files are in sync with their cloud blueprints!"));
3620
+ } else {
3621
+ console.log(chalk14.gray("To push local changes: lynxp push"));
3622
+ console.log(chalk14.gray("To pull cloud changes: lynxp pull <id>"));
3623
+ }
3624
+ console.log();
3625
+ }
3626
+ async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false) {
3627
+ const filePath = join11(cwd, file);
3628
+ if (!existsSync8(filePath)) {
3629
+ console.log(chalk14.red(`\u2717 File not found: ${file}`));
3321
3630
  return;
3322
3631
  }
3632
+ const spinner = compact ? null : ora11("Fetching blueprint...").start();
3633
+ try {
3634
+ const { blueprint } = await api.getBlueprint(blueprintId);
3635
+ spinner?.stop();
3636
+ if (!blueprint || !blueprint.content) {
3637
+ console.log(chalk14.red(`\u2717 Blueprint has no content`));
3638
+ return;
3639
+ }
3640
+ const localContent = await readFile9(filePath, "utf-8");
3641
+ const diff = computeDiff(blueprint.content, localContent);
3642
+ const stats = getDiffStats(diff);
3643
+ if (stats.added === 0 && stats.removed === 0) {
3644
+ if (!compact) {
3645
+ console.log(chalk14.green("\u2713 Files are identical!"));
3646
+ }
3647
+ } else {
3648
+ if (!compact) {
3649
+ console.log(chalk14.gray("Changes (cloud \u2192 local):"));
3650
+ console.log();
3651
+ }
3652
+ console.log(formatDiff(diff));
3653
+ console.log(chalk14.gray(` ${chalk14.green(`+${stats.added}`)} ${chalk14.red(`-${stats.removed}`)} lines`));
3654
+ }
3655
+ } catch (error) {
3656
+ spinner?.stop();
3657
+ if (error instanceof ApiRequestError) {
3658
+ console.log(chalk14.red(`\u2717 Could not fetch blueprint: ${error.message}`));
3659
+ } else {
3660
+ console.log(chalk14.red("\u2717 Failed to compare"));
3661
+ }
3662
+ }
3663
+ }
3664
+ async function diffWithBlueprintId(cwd, blueprintId) {
3323
3665
  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."));
3666
+ console.log(chalk14.yellow("\u26A0 Not logged in. Some blueprints may not be accessible."));
3667
+ console.log(chalk14.gray("Run 'lynxp login' to authenticate."));
3326
3668
  console.log();
3327
3669
  }
3328
- const spinner = ora10("Fetching blueprint...").start();
3670
+ const spinner = ora11("Fetching blueprint...").start();
3329
3671
  try {
3330
3672
  const { blueprint } = await api.getBlueprint(blueprintId);
3331
3673
  spinner.stop();
3332
3674
  if (!blueprint || !blueprint.content) {
3333
- console.log(chalk13.red(`\u2717 Blueprint not found or has no content: ${blueprintId}`));
3675
+ console.log(chalk14.red(`\u2717 Blueprint not found or has no content: ${blueprintId}`));
3334
3676
  return;
3335
3677
  }
3336
- console.log(chalk13.green(`\u2713 Blueprint: ${blueprint.name || blueprintId}`));
3678
+ console.log(chalk14.green(`\u2713 Blueprint: ${blueprint.name || blueprintId}`));
3337
3679
  if (blueprint.description) {
3338
- console.log(chalk13.gray(` ${blueprint.description}`));
3680
+ console.log(chalk14.gray(` ${blueprint.description}`));
3339
3681
  }
3340
3682
  console.log();
3341
3683
  const localPaths = [
@@ -3347,52 +3689,52 @@ async function diffCommand(blueprintId, options = {}) {
3347
3689
  ];
3348
3690
  let localContent = null;
3349
3691
  let localPath = null;
3350
- for (const path of localPaths) {
3351
- const fullPath = join11(cwd, path);
3692
+ for (const path2 of localPaths) {
3693
+ const fullPath = join11(cwd, path2);
3352
3694
  if (existsSync8(fullPath)) {
3353
3695
  try {
3354
- localContent = await readFile10(fullPath, "utf-8");
3355
- localPath = path;
3696
+ localContent = await readFile9(fullPath, "utf-8");
3697
+ localPath = path2;
3356
3698
  break;
3357
3699
  } catch {
3358
3700
  }
3359
3701
  }
3360
3702
  }
3361
3703
  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."));
3704
+ console.log(chalk14.yellow("\u26A0 No local AI configuration file found."));
3705
+ console.log(chalk14.gray("Run 'lynxp wizard' to create one, or 'lynxp pull' to download the blueprint."));
3364
3706
  return;
3365
3707
  }
3366
- console.log(chalk13.gray(`Comparing with: ${localPath}`));
3708
+ console.log(chalk14.gray(`Comparing with: ${localPath}`));
3367
3709
  console.log();
3368
3710
  const diff = computeDiff(blueprint.content, localContent);
3369
3711
  const stats = getDiffStats(diff);
3370
3712
  if (stats.added === 0 && stats.removed === 0) {
3371
- console.log(chalk13.green("\u2713 Files are identical!"));
3713
+ console.log(chalk14.green("\u2713 Files are identical!"));
3372
3714
  } else {
3373
- console.log(chalk13.gray("Changes (remote \u2192 local):"));
3715
+ console.log(chalk14.gray("Changes (remote \u2192 local):"));
3374
3716
  console.log();
3375
3717
  console.log(formatDiff(diff));
3376
3718
  console.log();
3377
- console.log(chalk13.gray(`Summary: ${chalk13.green(`+${stats.added}`)} ${chalk13.red(`-${stats.removed}`)} lines changed`));
3719
+ console.log(chalk14.gray(`Summary: ${chalk14.green(`+${stats.added}`)} ${chalk14.red(`-${stats.removed}`)} lines changed`));
3378
3720
  }
3379
3721
  console.log();
3380
3722
  } catch (error) {
3381
3723
  spinner.stop();
3382
3724
  if (error instanceof ApiRequestError) {
3383
3725
  if (error.statusCode === 401) {
3384
- console.log(chalk13.red("\u2717 Authentication required. Run 'lynxp login' first."));
3726
+ console.log(chalk14.red("\u2717 Authentication required. Run 'lynxp login' first."));
3385
3727
  } else if (error.statusCode === 404) {
3386
- console.log(chalk13.red(`\u2717 Blueprint not found: ${blueprintId}`));
3728
+ console.log(chalk14.red(`\u2717 Blueprint not found: ${blueprintId}`));
3387
3729
  } else if (error.statusCode === 403) {
3388
- console.log(chalk13.red("\u2717 Access denied to this blueprint."));
3730
+ console.log(chalk14.red("\u2717 Access denied to this blueprint."));
3389
3731
  } else {
3390
- console.log(chalk13.red(`\u2717 API error: ${error.message}`));
3732
+ console.log(chalk14.red(`\u2717 API error: ${error.message}`));
3391
3733
  }
3392
3734
  } else {
3393
- console.log(chalk13.red("\u2717 Failed to fetch blueprint"));
3735
+ console.log(chalk14.red("\u2717 Failed to fetch blueprint"));
3394
3736
  if (error instanceof Error) {
3395
- console.log(chalk13.gray(` ${error.message}`));
3737
+ console.log(chalk14.gray(` ${error.message}`));
3396
3738
  }
3397
3739
  }
3398
3740
  }
@@ -3400,22 +3742,22 @@ async function diffCommand(blueprintId, options = {}) {
3400
3742
  async function diffLocal(cwd) {
3401
3743
  const rulesDir = join11(cwd, ".lynxprompt/rules");
3402
3744
  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."));
3745
+ console.log(chalk14.yellow("\u26A0 No .lynxprompt/rules/ directory found."));
3746
+ console.log(chalk14.gray("Run 'lynxp init' to set up the advanced workflow, or 'lynxp wizard' for simple file generation."));
3405
3747
  return;
3406
3748
  }
3407
- console.log(chalk13.gray("Comparing .lynxprompt/rules/ with exported files..."));
3749
+ console.log(chalk14.gray("Comparing .lynxprompt/rules/ with exported files..."));
3408
3750
  console.log();
3409
3751
  const rulesPath = join11(rulesDir, "agents.md");
3410
3752
  if (!existsSync8(rulesPath)) {
3411
- console.log(chalk13.yellow("\u26A0 No rules files found in .lynxprompt/rules/"));
3753
+ console.log(chalk14.yellow("\u26A0 No rules files found in .lynxprompt/rules/"));
3412
3754
  return;
3413
3755
  }
3414
3756
  let rulesContent;
3415
3757
  try {
3416
- rulesContent = await readFile10(rulesPath, "utf-8");
3758
+ rulesContent = await readFile9(rulesPath, "utf-8");
3417
3759
  } catch {
3418
- console.log(chalk13.red("\u2717 Could not read .lynxprompt/rules/agents.md"));
3760
+ console.log(chalk14.red("\u2717 Could not read .lynxprompt/rules/agents.md"));
3419
3761
  return;
3420
3762
  }
3421
3763
  const exportedFiles = [
@@ -3427,7 +3769,7 @@ async function diffLocal(cwd) {
3427
3769
  const filePath = join11(cwd, file.path);
3428
3770
  if (existsSync8(filePath)) {
3429
3771
  try {
3430
- const exportedContent = await readFile10(filePath, "utf-8");
3772
+ const exportedContent = await readFile9(filePath, "utf-8");
3431
3773
  let compareContent = exportedContent;
3432
3774
  if (file.path.endsWith(".mdc")) {
3433
3775
  const frontmatterEnd = exportedContent.indexOf("---", 3);
@@ -3440,12 +3782,12 @@ async function diffLocal(cwd) {
3440
3782
  const stats = getDiffStats(diff);
3441
3783
  if (stats.added > 0 || stats.removed > 0) {
3442
3784
  hasChanges = true;
3443
- console.log(chalk13.yellow(`\u26A0 ${file.name} differs from source:`));
3785
+ console.log(chalk14.yellow(`\u26A0 ${file.name} differs from source:`));
3444
3786
  console.log(formatDiff(diff));
3445
- console.log(chalk13.gray(` ${chalk13.green(`+${stats.added}`)} ${chalk13.red(`-${stats.removed}`)} lines`));
3787
+ console.log(chalk14.gray(` ${chalk14.green(`+${stats.added}`)} ${chalk14.red(`-${stats.removed}`)} lines`));
3446
3788
  console.log();
3447
3789
  } else {
3448
- console.log(chalk13.green(`\u2713 ${file.name} is in sync`));
3790
+ console.log(chalk14.green(`\u2713 ${file.name} is in sync`));
3449
3791
  }
3450
3792
  } catch {
3451
3793
  }
@@ -3453,18 +3795,18 @@ async function diffLocal(cwd) {
3453
3795
  }
3454
3796
  if (!hasChanges) {
3455
3797
  console.log();
3456
- console.log(chalk13.green("\u2713 All exported files are in sync with .lynxprompt/rules/"));
3798
+ console.log(chalk14.green("\u2713 All exported files are in sync with .lynxprompt/rules/"));
3457
3799
  } else {
3458
3800
  console.log();
3459
- console.log(chalk13.gray("Run 'lynxp sync' to update exported files from .lynxprompt/rules/"));
3801
+ console.log(chalk14.gray("Run 'lynxp sync' to update exported files from .lynxprompt/rules/"));
3460
3802
  }
3461
3803
  console.log();
3462
3804
  }
3463
3805
 
3464
3806
  // src/commands/link.ts
3465
- import chalk14 from "chalk";
3466
- import ora11 from "ora";
3467
- import prompts6 from "prompts";
3807
+ import chalk15 from "chalk";
3808
+ import ora12 from "ora";
3809
+ import prompts7 from "prompts";
3468
3810
  import { join as join12 } from "path";
3469
3811
  import { existsSync as existsSync9 } from "fs";
3470
3812
  function getSourceFromVisibility2(visibility) {
@@ -3479,204 +3821,354 @@ function getSourceFromVisibility2(visibility) {
3479
3821
  return "marketplace";
3480
3822
  }
3481
3823
  }
3482
- async function linkCommand(file, blueprintId, options = {}) {
3824
+ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
3483
3825
  const cwd = process.cwd();
3484
3826
  if (options.list) {
3485
3827
  await listTrackedBlueprints(cwd);
3486
3828
  return;
3487
3829
  }
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;
3830
+ console.log();
3831
+ console.log(chalk15.cyan("\u{1F431} Link File to Blueprint"));
3832
+ console.log();
3833
+ let file;
3834
+ let blueprintId = blueprintIdArg;
3835
+ if (!fileArg) {
3836
+ const configFiles = [
3837
+ "AGENTS.md",
3838
+ "CLAUDE.md",
3839
+ ".cursor/rules/project.mdc",
3840
+ ".github/copilot-instructions.md",
3841
+ ".windsurfrules",
3842
+ ".zed/instructions.md",
3843
+ ".clinerules"
3844
+ ];
3845
+ const foundFiles = configFiles.filter((f) => existsSync9(join12(cwd, f)));
3846
+ if (foundFiles.length === 0) {
3847
+ console.log(chalk15.yellow("No AI configuration files found in this directory."));
3848
+ console.log();
3849
+ console.log(chalk15.gray("Create one first:"));
3850
+ console.log(chalk15.gray(" lynxp wizard Generate a new config file"));
3851
+ console.log(chalk15.gray(" lynxp pull <id> Download from marketplace"));
3852
+ return;
3853
+ }
3854
+ const { selectedFile } = await prompts7({
3855
+ type: "select",
3856
+ name: "selectedFile",
3857
+ message: "Which file do you want to link to a cloud blueprint?",
3858
+ choices: foundFiles.map((f) => ({
3859
+ title: f,
3860
+ value: f,
3861
+ description: "Local file exists"
3862
+ }))
3863
+ });
3864
+ if (!selectedFile) {
3865
+ console.log(chalk15.gray("Cancelled."));
3866
+ return;
3867
+ }
3868
+ file = selectedFile;
3869
+ } else {
3870
+ file = fileArg;
3509
3871
  }
3510
3872
  const filePath = join12(cwd, file);
3511
3873
  if (!existsSync9(filePath)) {
3512
- console.log(chalk14.red(`\u2717 File not found: ${file}`));
3874
+ console.log(chalk15.red(`\u2717 File not found: ${file}`));
3513
3875
  return;
3514
3876
  }
3515
3877
  const existing = await findBlueprintByFile(cwd, file);
3516
3878
  if (existing) {
3517
- console.log(chalk14.yellow(`\u26A0 This file is already linked to: ${existing.id}`));
3518
- const { proceed } = await prompts6({
3879
+ console.log(chalk15.yellow(`This file is already linked to: ${existing.name}`));
3880
+ console.log(chalk15.gray(` ID: ${existing.id}`));
3881
+ console.log();
3882
+ const { proceed } = await prompts7({
3519
3883
  type: "confirm",
3520
3884
  name: "proceed",
3521
3885
  message: "Replace the existing link?",
3522
3886
  initial: false
3523
3887
  });
3524
3888
  if (!proceed) {
3525
- console.log(chalk14.gray("Cancelled."));
3889
+ console.log(chalk15.gray("Cancelled."));
3526
3890
  return;
3527
3891
  }
3528
3892
  }
3529
- if (!isAuthenticated()) {
3530
- console.log(
3531
- chalk14.yellow("Not logged in. Run 'lynxp login' to authenticate.")
3532
- );
3533
- process.exit(1);
3893
+ if (!blueprintId) {
3894
+ if (!isAuthenticated()) {
3895
+ console.log(chalk15.yellow("You need to login to access your blueprints."));
3896
+ const { doLogin } = await prompts7({
3897
+ type: "confirm",
3898
+ name: "doLogin",
3899
+ message: "Login now?",
3900
+ initial: true
3901
+ });
3902
+ if (doLogin) {
3903
+ console.log(chalk15.gray("Run 'lynxp login' in another terminal, then come back here."));
3904
+ return;
3905
+ }
3906
+ console.log(chalk15.gray("Cancelled."));
3907
+ return;
3908
+ }
3909
+ const { searchMethod } = await prompts7({
3910
+ type: "select",
3911
+ name: "searchMethod",
3912
+ message: "How do you want to find the blueprint?",
3913
+ choices: [
3914
+ { title: "\u{1F4CB} From my blueprints", value: "list" },
3915
+ { title: "\u{1F50D} Search marketplace", value: "search" },
3916
+ { title: "\u{1F522} Enter ID directly", value: "manual" }
3917
+ ]
3918
+ });
3919
+ if (!searchMethod) {
3920
+ console.log(chalk15.gray("Cancelled."));
3921
+ return;
3922
+ }
3923
+ if (searchMethod === "list") {
3924
+ const spinner2 = ora12("Fetching your blueprints...").start();
3925
+ try {
3926
+ const { blueprints } = await api.listBlueprints();
3927
+ spinner2.stop();
3928
+ if (!blueprints || blueprints.length === 0) {
3929
+ console.log(chalk15.yellow("You don't have any blueprints yet."));
3930
+ console.log(chalk15.gray("Create one with 'lynxp push' or search the marketplace."));
3931
+ return;
3932
+ }
3933
+ const { selected } = await prompts7({
3934
+ type: "select",
3935
+ name: "selected",
3936
+ message: "Select a blueprint:",
3937
+ choices: blueprints.map((b) => ({
3938
+ title: b.name,
3939
+ value: b.id,
3940
+ description: b.description?.substring(0, 50) || ""
3941
+ }))
3942
+ });
3943
+ if (!selected) {
3944
+ console.log(chalk15.gray("Cancelled."));
3945
+ return;
3946
+ }
3947
+ blueprintId = selected;
3948
+ } catch {
3949
+ spinner2.stop();
3950
+ console.log(chalk15.red("\u2717 Could not fetch blueprints"));
3951
+ return;
3952
+ }
3953
+ } else if (searchMethod === "search") {
3954
+ const { query } = await prompts7({
3955
+ type: "text",
3956
+ name: "query",
3957
+ message: "Search for:"
3958
+ });
3959
+ if (!query) {
3960
+ console.log(chalk15.gray("Cancelled."));
3961
+ return;
3962
+ }
3963
+ const spinner2 = ora12(`Searching for "${query}"...`).start();
3964
+ try {
3965
+ const results = await api.searchBlueprints(query, 10);
3966
+ spinner2.stop();
3967
+ if (!results.templates || results.templates.length === 0) {
3968
+ console.log(chalk15.yellow(`No blueprints found for "${query}"`));
3969
+ return;
3970
+ }
3971
+ const { selected } = await prompts7({
3972
+ type: "select",
3973
+ name: "selected",
3974
+ message: "Select a blueprint:",
3975
+ choices: results.templates.map((b) => ({
3976
+ title: `${b.name} (\u2605 ${b.likes})`,
3977
+ value: b.id,
3978
+ description: b.author ? `by ${b.author}` : ""
3979
+ }))
3980
+ });
3981
+ if (!selected) {
3982
+ console.log(chalk15.gray("Cancelled."));
3983
+ return;
3984
+ }
3985
+ blueprintId = selected;
3986
+ } catch {
3987
+ spinner2.stop();
3988
+ console.log(chalk15.red("\u2717 Search failed"));
3989
+ return;
3990
+ }
3991
+ } else {
3992
+ const { manualId } = await prompts7({
3993
+ type: "text",
3994
+ name: "manualId",
3995
+ message: "Enter blueprint ID:"
3996
+ });
3997
+ if (!manualId) {
3998
+ console.log(chalk15.gray("Cancelled."));
3999
+ return;
4000
+ }
4001
+ blueprintId = manualId;
4002
+ }
3534
4003
  }
3535
- const spinner = ora11(`Fetching blueprint ${chalk14.cyan(blueprintId)}...`).start();
4004
+ if (!blueprintId) {
4005
+ console.log(chalk15.red("\u2717 No blueprint ID provided."));
4006
+ return;
4007
+ }
4008
+ const spinner = ora12(`Fetching blueprint ${chalk15.cyan(blueprintId)}...`).start();
3536
4009
  try {
3537
4010
  const { blueprint } = await api.getBlueprint(blueprintId);
3538
4011
  spinner.stop();
3539
4012
  const source = getSourceFromVisibility2(blueprint.visibility);
3540
4013
  const isMarketplace = source === "marketplace";
3541
4014
  console.log();
3542
- console.log(chalk14.cyan(`\u{1F431} Blueprint: ${chalk14.bold(blueprint.name)}`));
4015
+ console.log(chalk15.cyan(`\u{1F431} Blueprint: ${chalk15.bold(blueprint.name)}`));
3543
4016
  if (blueprint.description) {
3544
- console.log(chalk14.gray(` ${blueprint.description}`));
4017
+ console.log(chalk15.gray(` ${blueprint.description}`));
3545
4018
  }
3546
- console.log(chalk14.gray(` Visibility: ${blueprint.visibility}`));
4019
+ console.log(chalk15.gray(` Visibility: ${blueprint.visibility}`));
3547
4020
  console.log();
3548
4021
  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."));
4022
+ console.log(chalk15.yellow("\u26A0 This is a marketplace blueprint."));
4023
+ console.log(chalk15.gray(" Your local changes will NOT sync back to the cloud."));
4024
+ console.log(chalk15.gray(" To make changes, you'll need to create your own copy."));
3552
4025
  console.log();
3553
4026
  }
3554
- const { confirm } = await prompts6({
4027
+ const { confirm } = await prompts7({
3555
4028
  type: "confirm",
3556
4029
  name: "confirm",
3557
- message: `Link ${chalk14.cyan(file)} to ${chalk14.cyan(blueprint.name)}?`,
4030
+ message: `Link ${chalk15.cyan(file)} to ${chalk15.cyan(blueprint.name)}?`,
3558
4031
  initial: true
3559
4032
  });
3560
4033
  if (!confirm) {
3561
- console.log(chalk14.gray("Cancelled."));
4034
+ console.log(chalk15.gray("Cancelled."));
3562
4035
  return;
3563
4036
  }
3564
4037
  await linkBlueprint(cwd, file, blueprint.id, blueprint.name, source);
3565
4038
  console.log();
3566
- console.log(chalk14.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
4039
+ console.log(chalk15.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
3567
4040
  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`));
4041
+ console.log(chalk15.gray("Next steps:"));
4042
+ console.log(chalk15.gray(` \u2022 Run 'lynxp diff' to see differences`));
4043
+ console.log(chalk15.gray(` \u2022 Run 'lynxp status' to see all tracked blueprints`));
3572
4044
  if (!isMarketplace) {
3573
- console.log(chalk14.gray(` \u2022 Run 'lynxp push ${file}' to push local changes to cloud`));
4045
+ console.log(chalk15.gray(` \u2022 Run 'lynxp push' to push local changes to cloud`));
3574
4046
  }
3575
4047
  console.log();
3576
4048
  } catch (error) {
3577
4049
  spinner.stop();
3578
4050
  if (error instanceof ApiRequestError) {
3579
4051
  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."));
4052
+ console.log(chalk15.red(`\u2717 Blueprint not found: ${blueprintId}`));
4053
+ console.log(chalk15.gray(" Make sure the ID is correct. Use 'lynxp list' or 'lynxp search' to find blueprints."));
3582
4054
  } else if (error.statusCode === 403) {
3583
- console.log(chalk14.red("\u2717 You don't have access to this blueprint."));
4055
+ console.log(chalk15.red("\u2717 You don't have access to this blueprint."));
3584
4056
  } else {
3585
- console.log(chalk14.red(`\u2717 Error: ${error.message}`));
4057
+ console.log(chalk15.red(`\u2717 Error: ${error.message}`));
3586
4058
  }
3587
4059
  } else {
3588
- console.log(chalk14.red("\u2717 An unexpected error occurred."));
4060
+ console.log(chalk15.red("\u2717 An unexpected error occurred."));
3589
4061
  }
3590
4062
  }
3591
4063
  }
3592
- async function unlinkCommand(file) {
4064
+ async function unlinkCommand(fileArg) {
3593
4065
  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;
4066
+ console.log();
4067
+ console.log(chalk15.cyan("\u{1F431} Unlink File from Blueprint"));
4068
+ console.log();
4069
+ let file;
4070
+ if (!fileArg) {
4071
+ const status = await checkSyncStatus(cwd);
4072
+ if (status.length === 0) {
4073
+ console.log(chalk15.yellow("No files are currently linked to blueprints."));
4074
+ return;
4075
+ }
4076
+ const { selectedFile } = await prompts7({
4077
+ type: "select",
4078
+ name: "selectedFile",
4079
+ message: "Which file do you want to unlink?",
4080
+ choices: status.map(({ blueprint }) => ({
4081
+ title: blueprint.file,
4082
+ value: blueprint.file,
4083
+ description: `${blueprint.name} (${blueprint.source})`
4084
+ }))
4085
+ });
4086
+ if (!selectedFile) {
4087
+ console.log(chalk15.gray("Cancelled."));
4088
+ return;
4089
+ }
4090
+ file = selectedFile;
4091
+ } else {
4092
+ file = fileArg;
3600
4093
  }
3601
4094
  const tracked = await findBlueprintByFile(cwd, file);
3602
4095
  if (!tracked) {
3603
- console.log(chalk14.yellow(`\u26A0 File is not linked to any blueprint: ${file}`));
4096
+ console.log(chalk15.yellow(`File is not linked to any blueprint: ${file}`));
3604
4097
  return;
3605
4098
  }
4099
+ console.log(chalk15.gray(`Currently linked to: ${tracked.name}`));
4100
+ console.log(chalk15.gray(` ID: ${tracked.id}`));
4101
+ console.log(chalk15.gray(` Source: ${tracked.source}`));
3606
4102
  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({
4103
+ const { confirm } = await prompts7({
3612
4104
  type: "confirm",
3613
4105
  name: "confirm",
3614
- message: `Unlink ${chalk14.cyan(file)} from ${chalk14.cyan(tracked.name)}?`,
4106
+ message: `Unlink ${chalk15.cyan(file)} from ${chalk15.cyan(tracked.name)}?`,
3615
4107
  initial: true
3616
4108
  });
3617
4109
  if (!confirm) {
3618
- console.log(chalk14.gray("Cancelled."));
4110
+ console.log(chalk15.gray("Cancelled."));
3619
4111
  return;
3620
4112
  }
3621
4113
  const success = await untrackBlueprint(cwd, file);
3622
4114
  if (success) {
3623
4115
  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."));
4116
+ console.log(chalk15.green(`\u2705 Unlinked: ${file}`));
4117
+ console.log(chalk15.gray(" The file is now standalone. Changes won't sync with the cloud."));
3627
4118
  console.log();
3628
4119
  } else {
3629
- console.log(chalk14.red("\u2717 Failed to unlink file."));
4120
+ console.log(chalk15.red("\u2717 Failed to unlink file."));
3630
4121
  }
3631
4122
  }
3632
4123
  async function listTrackedBlueprints(cwd) {
3633
4124
  console.log();
3634
- console.log(chalk14.cyan("\u{1F431} Tracked Blueprints"));
4125
+ console.log(chalk15.cyan("\u{1F431} Tracked Blueprints"));
3635
4126
  console.log();
3636
4127
  const status = await checkSyncStatus(cwd);
3637
4128
  if (status.length === 0) {
3638
- console.log(chalk14.gray("No blueprints are currently tracked."));
4129
+ console.log(chalk15.gray("No blueprints are currently tracked."));
3639
4130
  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"));
4131
+ console.log(chalk15.gray("To track a blueprint:"));
4132
+ console.log(chalk15.gray(" lynxp pull <blueprint-id> Download and track a blueprint"));
4133
+ console.log(chalk15.gray(" lynxp link Link an existing file to a blueprint"));
3643
4134
  return;
3644
4135
  }
3645
4136
  for (const { blueprint, localModified, fileExists: fileExists2 } of status) {
3646
- const statusIcon = !fileExists2 ? chalk14.red("\u2717") : localModified ? chalk14.yellow("\u25CF") : chalk14.green("\u2713");
4137
+ const statusIcon = !fileExists2 ? chalk15.red("\u2717") : localModified ? chalk15.yellow("\u25CF") : chalk15.green("\u2713");
3647
4138
  const sourceLabel = {
3648
- marketplace: chalk14.gray("[marketplace]"),
3649
- team: chalk14.blue("[team]"),
3650
- private: chalk14.green("[private]"),
3651
- local: chalk14.gray("[local]")
4139
+ marketplace: chalk15.gray("[marketplace]"),
4140
+ team: chalk15.blue("[team]"),
4141
+ private: chalk15.green("[private]"),
4142
+ local: chalk15.gray("[local]")
3652
4143
  }[blueprint.source];
3653
- console.log(`${statusIcon} ${chalk14.cyan(blueprint.file)}`);
4144
+ console.log(`${statusIcon} ${chalk15.cyan(blueprint.file)}`);
3654
4145
  console.log(` ${sourceLabel} ${blueprint.name}`);
3655
- console.log(` ${chalk14.gray(`ID: ${blueprint.id}`)}`);
4146
+ console.log(` ${chalk15.gray(`ID: ${blueprint.id}`)}`);
3656
4147
  if (!fileExists2) {
3657
- console.log(chalk14.red(` \u26A0 File not found`));
4148
+ console.log(chalk15.red(` \u26A0 File not found`));
3658
4149
  } else if (localModified) {
3659
- console.log(chalk14.yellow(` \u26A0 Local changes detected`));
4150
+ console.log(chalk15.yellow(` \u26A0 Local changes detected`));
3660
4151
  }
3661
4152
  console.log();
3662
4153
  }
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`));
4154
+ console.log(chalk15.gray("Legend:"));
4155
+ console.log(chalk15.gray(` ${chalk15.green("\u2713")} In sync ${chalk15.yellow("\u25CF")} Modified locally ${chalk15.red("\u2717")} Missing`));
3665
4156
  console.log();
3666
4157
  }
3667
4158
 
3668
4159
  // src/index.ts
3669
4160
  var program = new Command();
3670
- program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version("0.2.0");
4161
+ program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version("0.3.0");
3671
4162
  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
4163
  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
4164
  program.command("status").description("Show current AI configuration and tracked blueprints").action(statusCommand);
3674
4165
  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
4166
  program.command("search <query>").description("Search public blueprints in the marketplace").option("-l, --limit <number>", "Number of results", "20").action(searchCommand);
3676
4167
  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);
4168
+ program.command("push [file]").description("Push local file to LynxPrompt cloud as a blueprint").option("-n, --name <name>", "Blueprint name").option("-d, --description <desc>", "Blueprint description").option("-v, --visibility <vis>", "Visibility: PRIVATE, TEAM, or PUBLIC", "PRIVATE").option("-t, --tags <tags>", "Tags (comma-separated)").option("-y, --yes", "Skip prompts").action(pushCommand);
3677
4169
  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);
4170
+ program.command("unlink [file]").description("Disconnect a local file from its cloud blueprint").action(unlinkCommand);
4171
+ program.command("diff [file-or-id]").description("Compare tracked files with their cloud blueprints").option("--local", "Compare .lynxprompt/rules/ with exported files").action(diffCommand);
3680
4172
  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
4173
  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
4174
  program.command("agents [action] [agent]").description("Manage AI agents (list, enable, disable, detect)").option("-i, --interactive", "Interactive agent selection").action(agentsCommand);
@@ -3686,32 +4178,33 @@ program.command("whoami").description("Show current authenticated user").action(
3686
4178
  program.addHelpText(
3687
4179
  "beforeAll",
3688
4180
  `
3689
- ${chalk15.cyan("\u{1F431} LynxPrompt CLI")} ${chalk15.gray("(also available as: lynxp)")}
3690
- ${chalk15.gray("Generate AI IDE configuration files from your terminal")}
4181
+ ${chalk16.cyan("\u{1F431} LynxPrompt CLI")} ${chalk16.gray("(also available as: lynxp)")}
4182
+ ${chalk16.gray("Generate AI IDE configuration files from your terminal")}
3691
4183
  `
3692
4184
  );
3693
4185
  program.addHelpText(
3694
4186
  "after",
3695
4187
  `
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/")}
4188
+ ${chalk16.cyan("Quick Start:")}
4189
+ ${chalk16.white("$ lynxp wizard")} ${chalk16.gray("Generate config interactively")}
4190
+ ${chalk16.white("$ lynxp wizard -y")} ${chalk16.gray("Generate AGENTS.md with defaults")}
4191
+ ${chalk16.white("$ lynxp wizard -f cursor")} ${chalk16.gray("Generate .cursor/rules/")}
3700
4192
 
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")}
4193
+ ${chalk16.cyan("Marketplace:")}
4194
+ ${chalk16.white("$ lynxp search nextjs")} ${chalk16.gray("Search blueprints")}
4195
+ ${chalk16.white("$ lynxp pull bp_abc123")} ${chalk16.gray("Download and track a blueprint")}
4196
+ ${chalk16.white("$ lynxp push")} ${chalk16.gray("Push local file to cloud")}
4197
+ ${chalk16.white("$ lynxp link --list")} ${chalk16.gray("Show tracked blueprints")}
3705
4198
 
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")}
4199
+ ${chalk16.cyan("Blueprint Tracking:")}
4200
+ ${chalk16.white("$ lynxp link AGENTS.md bp_xyz")} ${chalk16.gray("Link existing file to blueprint")}
4201
+ ${chalk16.white("$ lynxp unlink AGENTS.md")} ${chalk16.gray("Disconnect from cloud")}
4202
+ ${chalk16.white("$ lynxp diff bp_abc123")} ${chalk16.gray("Show changes vs cloud version")}
3710
4203
 
3711
- ${chalk15.cyan("CI/CD:")}
3712
- ${chalk15.white("$ lynxp check --ci")} ${chalk15.gray("Validate config (exit code)")}
4204
+ ${chalk16.cyan("CI/CD:")}
4205
+ ${chalk16.white("$ lynxp check --ci")} ${chalk16.gray("Validate config (exit code)")}
3713
4206
 
3714
- ${chalk15.gray("Docs: https://lynxprompt.com/docs/cli")}
4207
+ ${chalk16.gray("Docs: https://lynxprompt.com/docs/cli")}
3715
4208
  `
3716
4209
  );
3717
4210
  program.parse();