lynxprompt 2.1.38 → 2.1.40

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 chalk20 from "chalk";
5
+ import chalk21 from "chalk";
6
6
 
7
7
  // src/commands/login.ts
8
8
  import chalk from "chalk";
@@ -14,26 +14,29 @@ import Conf from "conf";
14
14
  import { chmodSync } from "fs";
15
15
  var config = new Conf({
16
16
  projectName: "lynxprompt",
17
- // The file holds the API token: readable by its owner only.
17
+ // The file holds API tokens: readable by its owner only.
18
18
  configFileMode: 384,
19
19
  schema: {
20
- token: {
21
- type: "string"
22
- },
23
- tokenApiUrl: {
24
- type: "string"
25
- },
26
20
  apiUrl: {
27
21
  type: "string",
28
22
  default: "https://lynxprompt.com"
29
23
  },
30
- user: {
24
+ tokens: {
31
25
  type: "object",
32
- properties: {
33
- id: { type: "string" },
34
- email: { type: "string" },
35
- name: { type: ["string", "null"] },
36
- plan: { type: "string" }
26
+ additionalProperties: {
27
+ type: "object",
28
+ properties: {
29
+ token: { type: "string" },
30
+ user: {
31
+ type: "object",
32
+ properties: {
33
+ id: { type: "string" },
34
+ email: { type: "string" },
35
+ name: { type: ["string", "null"] }
36
+ }
37
+ }
38
+ },
39
+ required: ["token"]
37
40
  }
38
41
  }
39
42
  },
@@ -48,38 +51,60 @@ try {
48
51
  function normalizeUrl(url) {
49
52
  return url.replace(/\/+$/, "");
50
53
  }
51
- if (config.get("token") && !config.get("tokenApiUrl")) {
52
- config.set("tokenApiUrl", normalizeUrl(config.get("apiUrl")));
54
+ function getLogins() {
55
+ return config.get("tokens") ?? {};
56
+ }
57
+ function setLogins(logins) {
58
+ if (Object.keys(logins).length === 0) {
59
+ config.delete("tokens");
60
+ } else {
61
+ config.set("tokens", logins);
62
+ }
63
+ }
64
+ var legacyToken = config.get("token");
65
+ if (legacyToken) {
66
+ const url = normalizeUrl(config.get("tokenApiUrl") ?? config.get("apiUrl"));
67
+ const logins = getLogins();
68
+ if (!logins[url]) {
69
+ const user = config.get("user");
70
+ logins[url] = user ? { token: legacyToken, user } : { token: legacyToken };
71
+ setLogins(logins);
72
+ }
73
+ }
74
+ for (const key of ["token", "tokenApiUrl", "user"]) {
75
+ if (config.has(key)) config.delete(key);
53
76
  }
54
- function storedTokenMatchesApiUrl() {
55
- return config.get("tokenApiUrl") === normalizeUrl(getApiUrl());
77
+ function currentLogin() {
78
+ return getLogins()[normalizeUrl(getApiUrl())];
56
79
  }
57
80
  function getToken() {
58
81
  const envToken = process.env.LYNXPROMPT_TOKEN;
59
82
  if (envToken) {
60
83
  return envToken;
61
84
  }
62
- if (!storedTokenMatchesApiUrl()) {
63
- return void 0;
64
- }
65
- return config.get("token");
85
+ return currentLogin()?.token;
66
86
  }
67
87
  function setToken(token) {
68
- config.set("token", token);
69
- config.set("tokenApiUrl", normalizeUrl(getApiUrl()));
88
+ const logins = getLogins();
89
+ logins[normalizeUrl(getApiUrl())] = { token };
90
+ setLogins(logins);
70
91
  }
71
92
  function getStoredCredentials() {
72
- const token = config.get("token");
73
- const apiUrl = config.get("tokenApiUrl");
74
- if (!token || !apiUrl) {
93
+ const apiUrl = normalizeUrl(getApiUrl());
94
+ const login = getLogins()[apiUrl];
95
+ if (!login) {
75
96
  return void 0;
76
97
  }
77
- return { token, apiUrl, email: config.get("user")?.email };
98
+ return { token: login.token, apiUrl, email: login.user?.email };
99
+ }
100
+ function getOtherLoggedInUrls() {
101
+ const current = normalizeUrl(getApiUrl());
102
+ return Object.keys(getLogins()).filter((url) => url !== current);
78
103
  }
79
104
  function clearToken() {
80
- config.delete("token");
81
- config.delete("tokenApiUrl");
82
- config.delete("user");
105
+ const logins = getLogins();
106
+ delete logins[normalizeUrl(getApiUrl())];
107
+ setLogins(logins);
83
108
  }
84
109
  function getApiUrl() {
85
110
  const envUrl = process.env.LYNXPROMPT_API_URL;
@@ -91,14 +116,21 @@ function getApiUrl() {
91
116
  function setApiUrl(url) {
92
117
  config.set("apiUrl", url);
93
118
  }
119
+ function resetApiUrl() {
120
+ config.reset("apiUrl");
121
+ return config.get("apiUrl");
122
+ }
94
123
  function getUser() {
95
- if (!process.env.LYNXPROMPT_TOKEN && !storedTokenMatchesApiUrl()) {
96
- return void 0;
97
- }
98
- return config.get("user");
124
+ return currentLogin()?.user;
99
125
  }
100
126
  function setUser(user) {
101
- config.set("user", user);
127
+ const url = normalizeUrl(getApiUrl());
128
+ const logins = getLogins();
129
+ if (!logins[url]) {
130
+ return;
131
+ }
132
+ logins[url] = { ...logins[url], user };
133
+ setLogins(logins);
102
134
  }
103
135
  function isAuthenticated() {
104
136
  return !!getToken();
@@ -473,7 +505,8 @@ import chalk2 from "chalk";
473
505
  async function logoutCommand() {
474
506
  const stored = getStoredCredentials();
475
507
  if (!stored) {
476
- console.log(chalk2.yellow("You are not currently logged in."));
508
+ console.log(chalk2.yellow(`You are not logged in at ${getApiUrl()}.`));
509
+ printOtherLogins();
477
510
  return;
478
511
  }
479
512
  const revoked = await api.revokeToken(stored.apiUrl, stored.token);
@@ -488,6 +521,13 @@ async function logoutCommand() {
488
521
  )
489
522
  );
490
523
  }
524
+ printOtherLogins();
525
+ }
526
+ function printOtherLogins() {
527
+ const others = getOtherLoggedInUrls();
528
+ if (others.length === 0) return;
529
+ console.log(chalk2.gray(` Still logged in at: ${others.join(", ")}`));
530
+ console.log(chalk2.gray(" Switch with 'lynxp config set-url <url>' and run 'lynxp logout' there."));
491
531
  }
492
532
 
493
533
  // src/commands/whoami.ts
@@ -505,8 +545,7 @@ async function whoamiCommand() {
505
545
  setUser({
506
546
  id: user.id,
507
547
  email: user.email,
508
- name: user.name,
509
- plan: user.plan
548
+ name: user.name
510
549
  });
511
550
  console.log();
512
551
  console.log(chalk3.cyan("\u{1F431} LynxPrompt Account"));
@@ -694,7 +733,11 @@ async function trackBlueprint(cwd, blueprint) {
694
733
  canPull,
695
734
  hierarchyId: blueprint.hierarchyId,
696
735
  hierarchyName: blueprint.hierarchyName,
697
- repositoryPath: blueprint.repositoryPath
736
+ repositoryPath: blueprint.repositoryPath,
737
+ ...blueprint.variables ? {
738
+ variables: blueprint.variables,
739
+ localChecksum: calculateChecksum(blueprint.localContent ?? blueprint.content)
740
+ } : {}
698
741
  });
699
742
  await saveBlueprints(cwd, config2);
700
743
  }
@@ -707,7 +750,7 @@ async function hasLocalChanges(cwd, tracked) {
707
750
  const filePath = join(cwd, tracked.file);
708
751
  const content = await readFile(filePath, "utf-8");
709
752
  const currentChecksum = calculateChecksum(content);
710
- return currentChecksum !== tracked.checksum;
753
+ return currentChecksum !== (tracked.localChecksum ?? tracked.checksum);
711
754
  } catch {
712
755
  return false;
713
756
  }
@@ -717,10 +760,15 @@ async function updateChecksum(cwd, file, content) {
717
760
  const blueprint = config2.blueprints.find((b) => sameFile(cwd, b.file, file));
718
761
  if (blueprint) {
719
762
  blueprint.checksum = calculateChecksum(content);
763
+ delete blueprint.variables;
764
+ delete blueprint.localChecksum;
720
765
  blueprint.pulledAt = (/* @__PURE__ */ new Date()).toISOString();
721
766
  await saveBlueprints(cwd, config2);
722
767
  }
723
768
  }
769
+ function conflictFilePath(cwd, file) {
770
+ return join(cwd, `${file}.remote`);
771
+ }
724
772
  async function untrackBlueprint(cwd, file) {
725
773
  const config2 = await loadBlueprints(cwd);
726
774
  const initialCount = config2.blueprints.length;
@@ -768,6 +816,563 @@ async function checkSyncStatus(cwd) {
768
816
  // src/utils/blueprint-files.ts
769
817
  import path from "path";
770
818
 
819
+ // ../packages/shared/src/platforms.ts
820
+ var PLATFORMS = [
821
+ // ========================================
822
+ // POPULAR - Featured platforms (shown first)
823
+ // ========================================
824
+ {
825
+ id: "universal",
826
+ name: "Universal (AGENTS.md)",
827
+ file: "AGENTS.md",
828
+ blueprintType: "AGENTS_MD",
829
+ icon: "\u{1F310}",
830
+ gradient: "from-violet-500 to-purple-500",
831
+ note: "Works with Claude Code, Copilot, Aider, and many others",
832
+ category: "popular",
833
+ format: "markdown",
834
+ featured: true,
835
+ url: "https://agents-md.dev"
836
+ },
837
+ {
838
+ id: "cursor",
839
+ name: "Cursor",
840
+ file: ".cursor/rules/project.mdc",
841
+ rulesDirectory: ".cursor/rules/",
842
+ blueprintType: "CURSOR_RULES",
843
+ icon: "\u26A1",
844
+ gradient: "from-blue-500 to-cyan-500",
845
+ note: "AI-powered code editor with native rules",
846
+ category: "popular",
847
+ format: "mdc",
848
+ featured: true,
849
+ url: "https://cursor.com"
850
+ },
851
+ {
852
+ id: "claude",
853
+ name: "Claude Code",
854
+ file: "CLAUDE.md",
855
+ blueprintType: "CLAUDE_MD",
856
+ icon: "\u{1F9E0}",
857
+ gradient: "from-orange-500 to-amber-500",
858
+ note: "Anthropic's agentic coding tool",
859
+ category: "popular",
860
+ format: "markdown",
861
+ featured: true,
862
+ url: "https://docs.anthropic.com/en/docs/claude-code"
863
+ },
864
+ {
865
+ id: "copilot",
866
+ name: "GitHub Copilot",
867
+ file: ".github/copilot-instructions.md",
868
+ blueprintType: "COPILOT_INSTRUCTIONS",
869
+ icon: "\u{1F419}",
870
+ gradient: "from-gray-600 to-gray-800",
871
+ note: "GitHub's AI pair programmer",
872
+ category: "popular",
873
+ format: "markdown",
874
+ featured: true,
875
+ url: "https://github.com/features/copilot"
876
+ },
877
+ {
878
+ id: "windsurf",
879
+ name: "Windsurf",
880
+ file: ".windsurfrules",
881
+ blueprintType: "WINDSURF_RULES",
882
+ icon: "\u{1F3C4}",
883
+ gradient: "from-teal-500 to-emerald-500",
884
+ note: "Codeium's AI-native IDE",
885
+ category: "popular",
886
+ format: "text",
887
+ featured: true,
888
+ url: "https://windsurf.com"
889
+ },
890
+ // ========================================
891
+ // AI-POWERED IDEs
892
+ // ========================================
893
+ {
894
+ id: "antigravity",
895
+ name: "Antigravity",
896
+ file: "GEMINI.md",
897
+ blueprintType: "GEMINI_MD",
898
+ icon: "\u{1F48E}",
899
+ gradient: "from-blue-600 to-indigo-600",
900
+ note: "Google's Gemini-powered IDE",
901
+ category: "ide",
902
+ format: "markdown",
903
+ featured: true,
904
+ url: "https://idx.google.com"
905
+ },
906
+ {
907
+ id: "zed",
908
+ name: "Zed",
909
+ file: ".rules",
910
+ blueprintType: "ZED_INSTRUCTIONS",
911
+ icon: "\u26A1",
912
+ gradient: "from-amber-500 to-yellow-500",
913
+ note: "High-performance editor with AI",
914
+ category: "ide",
915
+ format: "markdown",
916
+ url: "https://zed.dev"
917
+ },
918
+ {
919
+ id: "void",
920
+ name: "Void",
921
+ file: ".void/config.json",
922
+ blueprintType: "VOID_CONFIG",
923
+ icon: "\u{1F573}\uFE0F",
924
+ gradient: "from-slate-600 to-slate-800",
925
+ note: "Open-source Cursor alternative",
926
+ category: "ide",
927
+ format: "json",
928
+ url: "https://voideditor.com"
929
+ },
930
+ {
931
+ id: "trae",
932
+ name: "Trae AI",
933
+ file: ".trae/rules/project_rules.md",
934
+ rulesDirectory: ".trae/rules/",
935
+ blueprintType: "TRAE_RULES",
936
+ icon: "\u{1F537}",
937
+ gradient: "from-blue-400 to-blue-600",
938
+ note: "ByteDance's AI IDE",
939
+ category: "ide",
940
+ format: "markdown",
941
+ url: "https://trae.ai"
942
+ },
943
+ {
944
+ id: "firebase",
945
+ name: "Firebase Studio",
946
+ file: ".idx/airules.md",
947
+ blueprintType: "IDX_CONFIG",
948
+ icon: "\u{1F525}",
949
+ gradient: "from-yellow-500 to-orange-500",
950
+ note: "Google's cloud IDE",
951
+ category: "ide",
952
+ format: "markdown",
953
+ url: "https://idx.dev"
954
+ },
955
+ // ========================================
956
+ // EDITOR EXTENSIONS & PLUGINS
957
+ // ========================================
958
+ {
959
+ id: "cline",
960
+ name: "Cline",
961
+ file: ".clinerules",
962
+ blueprintType: "CLINE_RULES",
963
+ icon: "\u{1F527}",
964
+ gradient: "from-purple-500 to-pink-500",
965
+ note: "Autonomous coding agent for VS Code",
966
+ category: "editor",
967
+ format: "text",
968
+ url: "https://cline.bot"
969
+ },
970
+ {
971
+ id: "roocode",
972
+ name: "Roo Code",
973
+ file: ".roo/rules/project.md",
974
+ rulesDirectory: ".roo/rules/",
975
+ blueprintType: "ROO_RULES",
976
+ icon: "\u{1F998}",
977
+ gradient: "from-orange-400 to-red-500",
978
+ note: "AI coding assistant for VS Code",
979
+ category: "editor",
980
+ format: "markdown",
981
+ url: "https://roocode.com"
982
+ },
983
+ {
984
+ id: "continue",
985
+ name: "Continue",
986
+ file: ".continue/config.json",
987
+ blueprintType: "CONTINUE_CONFIG",
988
+ icon: "\u27A1\uFE0F",
989
+ gradient: "from-blue-600 to-indigo-500",
990
+ note: "Open-source AI autopilot",
991
+ category: "editor",
992
+ format: "json",
993
+ url: "https://continue.dev"
994
+ },
995
+ {
996
+ id: "cody",
997
+ name: "Sourcegraph Cody",
998
+ file: ".cody/config.json",
999
+ blueprintType: "CODY_CONFIG",
1000
+ icon: "\u{1F50D}",
1001
+ gradient: "from-pink-500 to-rose-500",
1002
+ note: "Context-aware AI assistant",
1003
+ category: "editor",
1004
+ format: "json",
1005
+ url: "https://sourcegraph.com/cody"
1006
+ },
1007
+ {
1008
+ id: "tabnine",
1009
+ name: "Tabnine",
1010
+ file: ".tabnine.yaml",
1011
+ blueprintType: "TABNINE_CONFIG",
1012
+ icon: "\u{1F4DD}",
1013
+ gradient: "from-cyan-500 to-blue-500",
1014
+ note: "AI code completion",
1015
+ category: "editor",
1016
+ format: "yaml",
1017
+ url: "https://tabnine.com"
1018
+ },
1019
+ {
1020
+ id: "supermaven",
1021
+ name: "Supermaven",
1022
+ file: ".supermaven/config.json",
1023
+ icon: "\u{1F9B8}",
1024
+ gradient: "from-amber-500 to-orange-500",
1025
+ note: "Fast AI code completions",
1026
+ category: "editor",
1027
+ format: "json",
1028
+ url: "https://supermaven.com"
1029
+ },
1030
+ {
1031
+ id: "codegpt",
1032
+ name: "CodeGPT",
1033
+ file: ".codegpt/config.json",
1034
+ icon: "\u{1F4AC}",
1035
+ gradient: "from-emerald-500 to-teal-500",
1036
+ note: "VS Code AI assistant",
1037
+ category: "editor",
1038
+ format: "json",
1039
+ url: "https://codegpt.co"
1040
+ },
1041
+ {
1042
+ id: "amazonq",
1043
+ name: "Amazon Q",
1044
+ file: ".amazonq/rules/project.md",
1045
+ rulesDirectory: ".amazonq/rules/",
1046
+ blueprintType: "AMAZONQ_RULES",
1047
+ icon: "\u{1F4E6}",
1048
+ gradient: "from-orange-400 to-amber-500",
1049
+ note: "AWS AI coding companion",
1050
+ category: "editor",
1051
+ format: "markdown",
1052
+ url: "https://aws.amazon.com/q/developer/"
1053
+ },
1054
+ {
1055
+ id: "augment",
1056
+ name: "Augment Code",
1057
+ file: ".augment/rules/project.md",
1058
+ rulesDirectory: ".augment/rules/",
1059
+ blueprintType: "AUGMENT_RULES",
1060
+ icon: "\u{1F52E}",
1061
+ gradient: "from-violet-500 to-purple-600",
1062
+ note: "AI code augmentation",
1063
+ category: "editor",
1064
+ format: "markdown",
1065
+ url: "https://augmentcode.com"
1066
+ },
1067
+ {
1068
+ id: "kilocode",
1069
+ name: "Kilo Code",
1070
+ file: ".kilocode/rules/project.md",
1071
+ rulesDirectory: ".kilocode/rules/",
1072
+ blueprintType: "KILOCODE_RULES",
1073
+ icon: "\u{1F4CA}",
1074
+ gradient: "from-green-500 to-emerald-500",
1075
+ note: "AI code generation",
1076
+ category: "editor",
1077
+ format: "markdown",
1078
+ url: "https://kilocode.ai"
1079
+ },
1080
+ {
1081
+ id: "junie",
1082
+ name: "Junie",
1083
+ file: ".junie/guidelines.md",
1084
+ blueprintType: "JUNIE_GUIDELINES",
1085
+ icon: "\u{1F3AF}",
1086
+ gradient: "from-pink-400 to-rose-500",
1087
+ note: "JetBrains AI assistant",
1088
+ category: "editor",
1089
+ format: "markdown",
1090
+ url: "https://www.jetbrains.com/junie/"
1091
+ },
1092
+ {
1093
+ id: "kiro",
1094
+ name: "Kiro",
1095
+ file: ".kiro/steering/project.md",
1096
+ rulesDirectory: ".kiro/steering/",
1097
+ blueprintType: "KIRO_STEERING",
1098
+ icon: "\u{1F680}",
1099
+ gradient: "from-blue-500 to-purple-500",
1100
+ note: "AWS spec-driven agent",
1101
+ category: "editor",
1102
+ format: "markdown",
1103
+ url: "https://kiro.dev"
1104
+ },
1105
+ // ========================================
1106
+ // CLI TOOLS
1107
+ // ========================================
1108
+ {
1109
+ id: "aider",
1110
+ name: "Aider",
1111
+ file: "CONVENTIONS.md",
1112
+ blueprintType: "AIDER_MD",
1113
+ icon: "\u{1F916}",
1114
+ gradient: "from-green-500 to-lime-500",
1115
+ note: "AI pair programming in terminal",
1116
+ category: "cli",
1117
+ format: "markdown",
1118
+ url: "https://aider.chat"
1119
+ },
1120
+ {
1121
+ id: "goose",
1122
+ name: "Goose",
1123
+ file: ".goosehints",
1124
+ blueprintType: "GOOSEHINTS",
1125
+ icon: "\u{1FABF}",
1126
+ gradient: "from-gray-500 to-gray-700",
1127
+ note: "Block's AI coding agent",
1128
+ category: "cli",
1129
+ format: "text",
1130
+ url: "https://github.com/block/goose"
1131
+ },
1132
+ {
1133
+ id: "warp",
1134
+ name: "Warp AI",
1135
+ file: "WARP.md",
1136
+ blueprintType: "WARP_MD",
1137
+ icon: "\u{1F680}",
1138
+ gradient: "from-purple-500 to-pink-500",
1139
+ note: "AI-powered terminal",
1140
+ category: "cli",
1141
+ format: "markdown",
1142
+ url: "https://warp.dev"
1143
+ },
1144
+ {
1145
+ id: "gemini-cli",
1146
+ name: "Gemini CLI",
1147
+ file: "GEMINI.md",
1148
+ blueprintType: "GEMINI_MD",
1149
+ icon: "\u{1F48E}",
1150
+ gradient: "from-blue-500 to-cyan-500",
1151
+ note: "Google's Gemini in terminal",
1152
+ category: "cli",
1153
+ format: "markdown",
1154
+ url: "https://github.com/google-gemini/gemini-cli"
1155
+ },
1156
+ {
1157
+ id: "opencode",
1158
+ name: "Open Code",
1159
+ file: "AGENTS.md",
1160
+ blueprintType: "OPENCODE_JSON",
1161
+ icon: "\u{1F513}",
1162
+ gradient: "from-gray-600 to-gray-800",
1163
+ note: "Open-source AI coding agent (reads AGENTS.md)",
1164
+ category: "cli",
1165
+ format: "markdown",
1166
+ url: "https://opencode.ai"
1167
+ },
1168
+ // ========================================
1169
+ // OTHER EMERGING TOOLS
1170
+ // ========================================
1171
+ {
1172
+ id: "openhands",
1173
+ name: "OpenHands",
1174
+ file: ".openhands/microagents/repo.md",
1175
+ blueprintType: "OPENHANDS_MD",
1176
+ icon: "\u{1F932}",
1177
+ gradient: "from-indigo-500 to-purple-500",
1178
+ note: "Open-source AI agent",
1179
+ category: "other",
1180
+ format: "markdown",
1181
+ url: "https://github.com/All-Hands-AI/OpenHands"
1182
+ },
1183
+ {
1184
+ id: "crush",
1185
+ name: "Crush",
1186
+ file: "CRUSH.md",
1187
+ icon: "\u{1F4A5}",
1188
+ gradient: "from-red-500 to-orange-500",
1189
+ note: "AI coding assistant",
1190
+ category: "other",
1191
+ format: "markdown"
1192
+ },
1193
+ {
1194
+ id: "firebender",
1195
+ name: "Firebender",
1196
+ file: ".firebender/rules/project.mdc",
1197
+ rulesDirectory: ".firebender/rules/",
1198
+ icon: "\u{1F525}",
1199
+ gradient: "from-orange-600 to-red-600",
1200
+ note: "AI code transformation",
1201
+ category: "other",
1202
+ format: "mdc"
1203
+ },
1204
+ // ========================================
1205
+ // AI AGENT COMMANDS - Slash commands for IDEs
1206
+ // ========================================
1207
+ {
1208
+ id: "cursor-command",
1209
+ name: "Cursor Command",
1210
+ file: ".cursor/commands/",
1211
+ blueprintType: "CURSOR_COMMAND",
1212
+ icon: "\u26A1",
1213
+ gradient: "from-blue-500 to-cyan-500",
1214
+ note: "Custom slash commands for Cursor IDE",
1215
+ category: "command",
1216
+ format: "markdown",
1217
+ isCommand: true,
1218
+ url: "https://docs.cursor.com/context/rules"
1219
+ },
1220
+ {
1221
+ id: "claude-command",
1222
+ name: "Claude Code Command",
1223
+ file: ".claude/commands/",
1224
+ blueprintType: "CLAUDE_COMMAND",
1225
+ icon: "\u{1F9E0}",
1226
+ gradient: "from-orange-500 to-amber-500",
1227
+ note: "Custom slash commands for Claude Code",
1228
+ category: "command",
1229
+ format: "markdown",
1230
+ isCommand: true,
1231
+ url: "https://docs.anthropic.com/en/docs/claude-code"
1232
+ },
1233
+ {
1234
+ id: "windsurf-workflow",
1235
+ name: "Windsurf Workflow",
1236
+ file: ".windsurf/workflows/",
1237
+ blueprintType: "WINDSURF_WORKFLOW",
1238
+ icon: "\u{1F3C4}",
1239
+ gradient: "from-teal-500 to-emerald-500",
1240
+ note: "Cascade workflows for Windsurf IDE",
1241
+ category: "command",
1242
+ format: "markdown",
1243
+ isCommand: true,
1244
+ url: "https://windsurf.com"
1245
+ },
1246
+ {
1247
+ id: "copilot-prompt",
1248
+ name: "Copilot Prompt",
1249
+ file: ".github/prompts/",
1250
+ blueprintType: "COPILOT_PROMPT",
1251
+ icon: "\u{1F419}",
1252
+ gradient: "from-gray-600 to-gray-800",
1253
+ note: "Custom prompts for GitHub Copilot Chat",
1254
+ category: "command",
1255
+ format: "markdown",
1256
+ isCommand: true,
1257
+ url: "https://github.com/features/copilot"
1258
+ },
1259
+ {
1260
+ id: "continue-prompt",
1261
+ name: "Continue Prompt",
1262
+ file: ".continue/prompts/",
1263
+ blueprintType: "CONTINUE_PROMPT",
1264
+ icon: "\u27A1\uFE0F",
1265
+ gradient: "from-blue-600 to-indigo-500",
1266
+ note: "Invokable prompts for Continue.dev",
1267
+ category: "command",
1268
+ format: "markdown",
1269
+ isCommand: true,
1270
+ url: "https://continue.dev"
1271
+ },
1272
+ {
1273
+ id: "opencode-command",
1274
+ name: "OpenCode Command",
1275
+ file: ".opencode/commands/",
1276
+ blueprintType: "OPENCODE_COMMAND",
1277
+ icon: "\u{1F513}",
1278
+ gradient: "from-gray-600 to-gray-800",
1279
+ note: "Custom commands for OpenCode TUI",
1280
+ category: "command",
1281
+ format: "markdown",
1282
+ isCommand: true,
1283
+ url: "https://opencode.ai"
1284
+ }
1285
+ ];
1286
+ var COMMANDS = [
1287
+ {
1288
+ id: "cursor-command",
1289
+ name: "Cursor Command",
1290
+ directory: ".cursor/commands",
1291
+ extension: ".md",
1292
+ platformId: "cursor",
1293
+ icon: "\u26A1",
1294
+ gradient: "from-blue-500 to-cyan-500",
1295
+ note: "Custom slash commands for Cursor IDE (e.g., /deploy, /test)"
1296
+ },
1297
+ {
1298
+ id: "claude-command",
1299
+ name: "Claude Code Command",
1300
+ directory: ".claude/commands",
1301
+ extension: ".md",
1302
+ platformId: "claude",
1303
+ icon: "\u{1F9E0}",
1304
+ gradient: "from-orange-500 to-amber-500",
1305
+ note: "Custom slash commands for Claude Code (e.g., /review, /refactor)"
1306
+ },
1307
+ {
1308
+ id: "windsurf-workflow",
1309
+ name: "Windsurf Workflow",
1310
+ directory: ".windsurf/workflows",
1311
+ extension: ".md",
1312
+ platformId: "windsurf",
1313
+ icon: "\u{1F3C4}",
1314
+ gradient: "from-teal-500 to-emerald-500",
1315
+ note: "Cascade workflows for Windsurf IDE"
1316
+ },
1317
+ {
1318
+ id: "copilot-prompt",
1319
+ name: "Copilot Prompt",
1320
+ directory: ".github/prompts",
1321
+ extension: ".prompt.md",
1322
+ platformId: "copilot",
1323
+ icon: "\u{1F419}",
1324
+ gradient: "from-gray-600 to-gray-800",
1325
+ note: "Custom prompts for GitHub Copilot Chat"
1326
+ },
1327
+ {
1328
+ id: "continue-prompt",
1329
+ name: "Continue Prompt",
1330
+ directory: ".continue/prompts",
1331
+ extension: ".md",
1332
+ platformId: "continue",
1333
+ icon: "\u27A1\uFE0F",
1334
+ gradient: "from-blue-600 to-indigo-500",
1335
+ note: "Invokable prompts for Continue.dev extension"
1336
+ },
1337
+ {
1338
+ id: "opencode-command",
1339
+ name: "OpenCode Command",
1340
+ directory: ".opencode/commands",
1341
+ extension: ".md",
1342
+ platformId: "opencode",
1343
+ icon: "\u{1F513}",
1344
+ gradient: "from-gray-600 to-gray-800",
1345
+ note: "Custom commands for OpenCode TUI"
1346
+ }
1347
+ ];
1348
+ function getPlatform(id) {
1349
+ return PLATFORMS.find((p) => p.id === id);
1350
+ }
1351
+ var PLATFORM_COUNT = PLATFORMS.length;
1352
+ var PLATFORM_FILES = Object.fromEntries(
1353
+ PLATFORMS.map((p) => [p.id, p.file])
1354
+ );
1355
+ function getCommandTemplateType(commandId) {
1356
+ const platform2 = getPlatform(commandId);
1357
+ return platform2?.isCommand ? platform2.blueprintType : void 0;
1358
+ }
1359
+ var BLUEPRINT_TYPE_ALIASES = {
1360
+ CURSOR_MDC: "cursor",
1361
+ CURSORRULES: "cursor",
1362
+ // .cursorrules is deprecated; Cursor reads .cursor/rules/
1363
+ CODEX_MD: "universal",
1364
+ // Codex reads AGENTS.md
1365
+ FIREBASE_RULES: "firebase",
1366
+ GOOSE_HINTS: "goose",
1367
+ OPENCODE_CONFIG: "opencode",
1368
+ OPENHANDS_CONFIG: "openhands",
1369
+ FIREBENDER_CONFIG: "firebender"
1370
+ };
1371
+ function platformForBlueprintType(type) {
1372
+ return PLATFORMS.find((p) => p.blueprintType === type)?.id ?? BLUEPRINT_TYPE_ALIASES[type];
1373
+ }
1374
+ var COMMAND_COUNT = COMMANDS.length;
1375
+
771
1376
  // src/utils/generator.ts
772
1377
  function bpVar(blueprintMode, varName, defaultValue) {
773
1378
  if (!blueprintMode || !defaultValue) return defaultValue;
@@ -792,47 +1397,9 @@ function parseVariablesString(varsStr) {
792
1397
  }
793
1398
  return variables;
794
1399
  }
795
- var PLATFORM_FILES = {
796
- // Popular platforms
797
- universal: "AGENTS.md",
798
- agents: "AGENTS.md",
799
- // Alias for universal
800
- cursor: ".cursor/rules/project.mdc",
801
- claude: "CLAUDE.md",
802
- copilot: ".github/copilot-instructions.md",
803
- windsurf: ".windsurfrules",
804
- // AI-powered IDEs
805
- antigravity: "GEMINI.md",
806
- zed: ".zed/instructions.md",
807
- void: ".void/config.json",
808
- trae: ".trae/rules/project_rules.md",
809
- // Trae reads project_rules.md
810
- firebase: ".idx/rules/project.mdc",
811
- // Editor extensions
812
- cline: ".clinerules",
813
- roocode: ".roo/rules/project.mdc",
814
- continue: ".continue/config.json",
815
- cody: ".cody/config.json",
816
- tabnine: ".tabnine.yaml",
817
- supermaven: ".supermaven/config.json",
818
- codegpt: ".codegpt/config.json",
819
- amazonq: ".amazonq/rules/project.md",
820
- augment: ".augment/rules/project.md",
821
- // Augment rules are .md with a `type` frontmatter
822
- kilocode: ".kilocode/rules/project.mdc",
823
- junie: ".junie/guidelines.md",
824
- kiro: ".kiro/steering/project.md",
825
- // Kiro steering files are .md with an `inclusion` frontmatter
826
- // CLI tools
827
- aider: ".aider.conf.yml",
828
- goose: ".goosehints",
829
- warp: "WARP.md",
830
- "gemini-cli": "GEMINI.md",
831
- opencode: "opencode.json",
832
- // Other emerging tools
833
- openhands: ".openhands/microagents/repo.md",
834
- crush: "CRUSH.md",
835
- firebender: "firebender.json"
1400
+ var PLATFORM_FILES2 = {
1401
+ ...Object.fromEntries(PLATFORMS.filter((p) => !p.isCommand).map((p) => [p.id, p.file])),
1402
+ agents: "AGENTS.md"
836
1403
  };
837
1404
  var PERSONA_DESCRIPTIONS = {
838
1405
  backend: "a senior backend developer specializing in APIs, databases, and microservices architecture",
@@ -1294,7 +1861,7 @@ var STATIC_FILE_PATHS = {
1294
1861
  function generateConfig(options) {
1295
1862
  const files = {};
1296
1863
  for (const platform2 of options.platforms) {
1297
- const filename = PLATFORM_FILES[platform2];
1864
+ const filename = PLATFORM_FILES2[platform2];
1298
1865
  if (filename) {
1299
1866
  files[filename] = generateFileContent(options, platform2);
1300
1867
  }
@@ -1326,12 +1893,10 @@ function generateConfig(options) {
1326
1893
  }
1327
1894
  function generateFileContent(options, platform2) {
1328
1895
  const sections = [];
1329
- const mdcPlatforms = ["cursor", "firebase", "roocode", "kilocode"];
1330
- const yamlPlatforms = ["aider", "tabnine"];
1331
- const jsonPlatforms = ["void", "continue", "cody", "supermaven", "codegpt", "opencode", "firebender"];
1332
- const isMdc = mdcPlatforms.includes(platform2);
1333
- const isYaml = yamlPlatforms.includes(platform2);
1334
- const isJson = jsonPlatforms.includes(platform2);
1896
+ const format = getPlatform(platform2)?.format ?? "markdown";
1897
+ const isMdc = format === "mdc";
1898
+ const isYaml = format === "yaml";
1899
+ const isJson = format === "json";
1335
1900
  const isMarkdown = !isMdc && !isYaml && !isJson;
1336
1901
  const bp = options.blueprintMode || false;
1337
1902
  if (isYaml) {
@@ -1393,7 +1958,7 @@ function generateFileContent(options, platform2) {
1393
1958
  sections.push("# Install CLI: npm install -g lynxprompt");
1394
1959
  sections.push("# Login: lynxp login");
1395
1960
  } else {
1396
- const fileName = platform2 === "cursor" ? ".cursor/rules/agents.mdc" : "AGENTS.md";
1961
+ const fileName = PLATFORM_FILES2[platform2] ?? "AGENTS.md";
1397
1962
  sections.push("");
1398
1963
  sections.push(`# Using curl with environment variable (token stored in $${tokenEnvVar}):`);
1399
1964
  sections.push("");
@@ -2312,32 +2877,13 @@ function generateJsonConfig(options, platform2) {
2312
2877
  config2.rules = rules;
2313
2878
  } else if (platform2 === "codegpt") {
2314
2879
  config2.systemPrompt = rules.join("\n");
2315
- } else if (platform2 === "opencode") {
2316
- config2.instructions = rules;
2317
- } else if (platform2 === "firebender") {
2318
- config2.transforms = rules;
2319
2880
  }
2320
2881
  config2._comment = `Generated by LynxPrompt CLI for ${options.name}`;
2321
2882
  return JSON.stringify(config2, null, 2);
2322
2883
  }
2323
2884
  function generateYamlConfig(options, platform2) {
2324
2885
  const lines = [];
2325
- if (platform2 === "aider") {
2326
- lines.push("# Aider configuration");
2327
- lines.push(`# Project: ${options.name}`);
2328
- lines.push("");
2329
- lines.push("# Code style");
2330
- if (options.stack.includes("typescript") || options.stack.includes("javascript")) {
2331
- lines.push("auto-lint: true");
2332
- }
2333
- lines.push("");
2334
- lines.push("# Custom instructions");
2335
- lines.push("read:");
2336
- lines.push(" - README.md");
2337
- if (options.importantFiles?.includes("architecture") || options.importantFiles?.includes("architecture_md")) {
2338
- lines.push(" - ARCHITECTURE.md");
2339
- }
2340
- } else if (platform2 === "tabnine") {
2886
+ if (platform2 === "tabnine") {
2341
2887
  lines.push("# Tabnine configuration");
2342
2888
  lines.push(`# Project: ${options.name}`);
2343
2889
  lines.push("");
@@ -2366,57 +2912,6 @@ function generateYamlConfig(options, platform2) {
2366
2912
  }
2367
2913
 
2368
2914
  // src/utils/blueprint-files.ts
2369
- var TYPE_TO_PLATFORM = {
2370
- AGENTS_MD: "universal",
2371
- CURSOR_RULES: "cursor",
2372
- CURSOR_MDC: "cursor",
2373
- CURSORRULES: "cursor",
2374
- // .cursorrules is deprecated; Cursor reads .cursor/rules/
2375
- CLAUDE_MD: "claude",
2376
- COPILOT_INSTRUCTIONS: "copilot",
2377
- WINDSURF_RULES: "windsurf",
2378
- GEMINI_MD: "antigravity",
2379
- CLINE_RULES: "cline",
2380
- ZED_INSTRUCTIONS: "zed",
2381
- VOID_CONFIG: "void",
2382
- TRAE_RULES: "trae",
2383
- IDX_CONFIG: "firebase",
2384
- FIREBASE_RULES: "firebase",
2385
- ROO_RULES: "roocode",
2386
- CONTINUE_CONFIG: "continue",
2387
- CODY_CONFIG: "cody",
2388
- TABNINE_CONFIG: "tabnine",
2389
- SUPERMAVEN_CONFIG: "supermaven",
2390
- CODEGPT_CONFIG: "codegpt",
2391
- AMAZONQ_RULES: "amazonq",
2392
- AUGMENT_RULES: "augment",
2393
- KILOCODE_RULES: "kilocode",
2394
- JUNIE_GUIDELINES: "junie",
2395
- KIRO_STEERING: "kiro",
2396
- GOOSEHINTS: "goose",
2397
- GOOSE_HINTS: "goose",
2398
- WARP_MD: "warp",
2399
- OPENCODE_JSON: "opencode",
2400
- OPENCODE_CONFIG: "opencode",
2401
- OPENHANDS_MD: "openhands",
2402
- OPENHANDS_CONFIG: "openhands",
2403
- CRUSH_MD: "crush",
2404
- FIREBENDER_CONFIG: "firebender"
2405
- };
2406
- var TYPE_TO_FIXED_FILE = {
2407
- CODEX_MD: "AGENTS.md",
2408
- // Codex reads AGENTS.md
2409
- AIDER_MD: "AIDER.md"
2410
- // Markdown conventions, not the .aider.conf.yml settings file
2411
- };
2412
- var COMMAND_TYPE_DIRS = {
2413
- CURSOR_COMMAND: ".cursor/commands",
2414
- CLAUDE_COMMAND: ".claude/commands",
2415
- WINDSURF_WORKFLOW: ".windsurf/workflows",
2416
- COPILOT_PROMPT: ".copilot/prompts",
2417
- CONTINUE_PROMPT: ".continue/prompts",
2418
- OPENCODE_COMMAND: ".opencode/commands"
2419
- };
2420
2915
  var FALLBACK_FILE = "ai-config.md";
2421
2916
  function slugify(name) {
2422
2917
  const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -2424,11 +2919,10 @@ function slugify(name) {
2424
2919
  }
2425
2920
  function blueprintFilename(bp) {
2426
2921
  if (bp.repository_path) return bp.repository_path;
2427
- const platform2 = TYPE_TO_PLATFORM[bp.type];
2428
- if (platform2 && PLATFORM_FILES[platform2]) return PLATFORM_FILES[platform2];
2429
- if (TYPE_TO_FIXED_FILE[bp.type]) return TYPE_TO_FIXED_FILE[bp.type];
2430
- const commandDir = COMMAND_TYPE_DIRS[bp.type];
2431
- if (commandDir) return `${commandDir}/${slugify(bp.name || "")}.md`;
2922
+ const platform2 = platformForBlueprintType(bp.type);
2923
+ if (platform2 && PLATFORM_FILES2[platform2]) return PLATFORM_FILES2[platform2];
2924
+ const command = COMMANDS.find((c) => c.id === platform2);
2925
+ if (command) return `${command.directory}/${slugify(bp.name || "")}${command.extension}`;
2432
2926
  return FALLBACK_FILE;
2433
2927
  }
2434
2928
  function safePath(root, untrusted, pathImpl = path) {
@@ -2441,8 +2935,88 @@ function safePath(root, untrusted, pathImpl = path) {
2441
2935
  }
2442
2936
  return resolved;
2443
2937
  }
2938
+ function normPath(p) {
2939
+ return p.replace(/\\/g, "/").toLowerCase();
2940
+ }
2941
+ function basename(p) {
2942
+ return p.replace(/\\/g, "/").split("/").pop() || "";
2943
+ }
2944
+ var RULE_PLATFORMS = PLATFORMS.filter((p) => !p.isCommand);
2945
+ var typeOf = (p) => p.blueprintType ?? "CUSTOM";
2946
+ var atPath = (relativePath, file) => {
2947
+ const p = normPath(relativePath);
2948
+ return p === file || p.endsWith(`/${file}`);
2949
+ };
2950
+ var inFolder = (relativePath, folder) => {
2951
+ const p = normPath(relativePath);
2952
+ return p.startsWith(folder) || p.includes(`/${folder}`);
2953
+ };
2954
+ var CONFIG_PATTERNS = [
2955
+ // Commands: one file per command in the tool's folder
2956
+ ...COMMANDS.map((c) => ({
2957
+ match: (p) => inFolder(p, `${c.directory}/`) && p.endsWith(c.extension),
2958
+ type: getCommandTemplateType(c.id) ?? "CUSTOM",
2959
+ label: c.name
2960
+ })),
2961
+ // Folders the tool reads every rule file from
2962
+ ...RULE_PLATFORMS.filter((p) => p.rulesDirectory).map((p) => ({
2963
+ match: (rel) => inFolder(rel, p.rulesDirectory) && /\.mdc?$/.test(rel),
2964
+ type: typeOf(p),
2965
+ label: `${p.name} Rules`
2966
+ })),
2967
+ // A file at a fixed path or with a fixed name, wherever it sits
2968
+ ...RULE_PLATFORMS.map((p) => ({
2969
+ match: (rel) => p.file.includes("/") ? atPath(rel, p.file) : basename(rel) === p.file,
2970
+ type: typeOf(p),
2971
+ label: p.name
2972
+ })),
2973
+ // Paths older CLI versions generated
2974
+ { match: (p) => inFolder(p, ".copilot/prompts/") && p.endsWith(".md"), type: "COPILOT_PROMPT", label: "Copilot Prompt" },
2975
+ { match: (p) => inFolder(p, ".idx/") && p.endsWith(".mdc"), type: "IDX_CONFIG", label: "Firebase Studio" },
2976
+ { match: (p) => atPath(p, ".zed/instructions.md"), type: "ZED_INSTRUCTIONS", label: "Zed" },
2977
+ { match: (p) => basename(p) === "AIDER.md", type: "AIDER_MD", label: "AIDER.md" },
2978
+ { match: (p) => basename(p) === ".cursorrules", type: "CURSORRULES", label: "Cursor Rules (legacy)" },
2979
+ { match: (p) => basename(p) === "opencode.json", type: "OPENCODE_JSON", label: "OpenCode Config" },
2980
+ { match: (p) => basename(p) === "firebender.json", type: "CUSTOM", label: "Firebender Config" }
2981
+ ];
2982
+ function matchConfigFile(relativePath) {
2983
+ for (const pattern of CONFIG_PATTERNS) {
2984
+ if (pattern.match(relativePath)) {
2985
+ return { type: pattern.type, label: pattern.label };
2986
+ }
2987
+ }
2988
+ return null;
2989
+ }
2990
+
2991
+ // src/utils/variables.ts
2992
+ var VARIABLE_PATTERN_SOURCE = String.raw`\[\[([A-Za-z_][A-Za-z0-9_]*)(?:\|((?:(?!\[\[)[\s\S])*?)\]\](?!\])|\]\])`;
2993
+ function fillBlueprintVariables(content, values) {
2994
+ const missing = /* @__PURE__ */ new Set();
2995
+ const filled = content.replace(
2996
+ new RegExp(VARIABLE_PATTERN_SOURCE, "g"),
2997
+ (match, name, fallback) => {
2998
+ const key = name.toUpperCase();
2999
+ if (Object.prototype.hasOwnProperty.call(values, key)) return values[key];
3000
+ if (fallback !== void 0) return fallback;
3001
+ missing.add(key);
3002
+ return match;
3003
+ }
3004
+ );
3005
+ return { content: filled, missing: [...missing] };
3006
+ }
2444
3007
 
2445
3008
  // src/commands/pull.ts
3009
+ function contentToWrite(content, options, label) {
3010
+ if (options.vars === void 0) return content;
3011
+ const { content: filled, missing } = fillBlueprintVariables(content, parseVariablesString(options.vars));
3012
+ if (missing.length > 0) {
3013
+ console.log(chalk5.yellow(` \u26A0 ${label}: no value for ${missing.join(", ")} (left as [[...]])`));
3014
+ }
3015
+ return filled;
3016
+ }
3017
+ function pulledVariables(options, written, content) {
3018
+ return options.vars === void 0 || written === content ? void 0 : parseVariablesString(options.vars);
3019
+ }
2446
3020
  function getSourceFromVisibility(visibility) {
2447
3021
  switch (visibility) {
2448
3022
  case "PUBLIC":
@@ -2548,7 +3122,8 @@ async function pullHierarchy(id, options) {
2548
3122
  if (dir !== "." && dir !== options.output) {
2549
3123
  await mkdir2(dir, { recursive: true });
2550
3124
  }
2551
- await writeFile2(outputPath, blueprint.content, "utf-8");
3125
+ const written = contentToWrite(blueprint.content, options, filename);
3126
+ await writeFile2(outputPath, written, "utf-8");
2552
3127
  if (options.track !== false) {
2553
3128
  const source = getSourceFromVisibility(blueprint.visibility);
2554
3129
  await trackBlueprint(cwd, {
@@ -2560,7 +3135,9 @@ async function pullHierarchy(id, options) {
2560
3135
  source,
2561
3136
  hierarchyId: id,
2562
3137
  hierarchyName: hierarchy.name,
2563
- repositoryPath: bp.repository_path || void 0
3138
+ repositoryPath: bp.repository_path || void 0,
3139
+ localContent: written,
3140
+ variables: pulledVariables(options, written, blueprint.content)
2564
3141
  });
2565
3142
  }
2566
3143
  console.log(chalk5.green(` \u2713 ${filename}`));
@@ -2729,7 +3306,8 @@ async function pullBlueprint(id, options) {
2729
3306
  if (dir !== ".") {
2730
3307
  await mkdir2(dir, { recursive: true });
2731
3308
  }
2732
- await writeFile2(outputPath, blueprint.content, "utf-8");
3309
+ const written = contentToWrite(blueprint.content, options, filename);
3310
+ await writeFile2(outputPath, written, "utf-8");
2733
3311
  if (options.track !== false) {
2734
3312
  await trackBlueprint(cwd, {
2735
3313
  id: blueprint.id,
@@ -2738,7 +3316,9 @@ async function pullBlueprint(id, options) {
2738
3316
  content: blueprint.content,
2739
3317
  source,
2740
3318
  hierarchyId: blueprint.hierarchy_id || void 0,
2741
- repositoryPath: blueprint.repository_path || void 0
3319
+ repositoryPath: blueprint.repository_path || void 0,
3320
+ localContent: written,
3321
+ variables: pulledVariables(options, written, blueprint.content)
2742
3322
  });
2743
3323
  }
2744
3324
  console.log(chalk5.green(`\u2705 Downloaded: ${chalk5.bold(outputPath)}`));
@@ -2747,8 +3327,8 @@ async function pullBlueprint(id, options) {
2747
3327
  if (blueprint.content_checksum) {
2748
3328
  console.log(chalk5.gray(` Checksum: ${blueprint.content_checksum}`));
2749
3329
  }
2750
- if (isMarketplace) {
2751
- console.log(chalk5.gray(` Updates: Run 'lynxp pull ${id}' to sync updates`));
3330
+ if (isMarketplace || options.vars !== void 0) {
3331
+ console.log(chalk5.gray(` Updates: Run 'lynxp sync ${trackedFile}' to get cloud updates`));
2752
3332
  } else {
2753
3333
  console.log(chalk5.gray(` Sync: Run 'lynxp push ${trackedFile}' to push changes`));
2754
3334
  }
@@ -2875,59 +3455,6 @@ function blueprintUrl(id) {
2875
3455
  return `${getApiUrl().replace(/\/+$/, "")}/templates/${id.replace("bp_", "")}`;
2876
3456
  }
2877
3457
  var VISIBILITIES = ["PRIVATE", "TEAM", "PUBLIC"];
2878
- function normPath(p) {
2879
- return p.replace(/\\/g, "/").toLowerCase();
2880
- }
2881
- var CONFIG_PATTERNS = [
2882
- // Commands (most specific paths first)
2883
- { match: (p) => normPath(p).includes(".cursor/commands/") && p.endsWith(".md"), type: "CURSOR_COMMAND", label: "Cursor Command" },
2884
- { match: (p) => normPath(p).includes(".claude/commands/") && p.endsWith(".md"), type: "CLAUDE_COMMAND", label: "Claude Command" },
2885
- { match: (p) => normPath(p).includes(".windsurf/workflows/") && p.endsWith(".md"), type: "WINDSURF_WORKFLOW", label: "Windsurf Workflow" },
2886
- { match: (p) => normPath(p).includes(".copilot/prompts/") && p.endsWith(".md"), type: "COPILOT_PROMPT", label: "Copilot Prompt" },
2887
- { match: (p) => normPath(p).includes(".continue/prompts/") && p.endsWith(".md"), type: "CONTINUE_PROMPT", label: "Continue Prompt" },
2888
- { match: (p) => normPath(p).includes(".opencode/commands/") && p.endsWith(".md"), type: "OPENCODE_COMMAND", label: "OpenCode Command" },
2889
- // Directory-based rules
2890
- { match: (p) => normPath(p).includes(".cursor/rules/") && p.endsWith(".mdc"), type: "CURSOR_RULES", label: "Cursor Rules" },
2891
- { match: (p) => normPath(p).includes(".trae/rules/") && /\.mdc?$/.test(p), type: "TRAE_RULES", label: "Trae Rules" },
2892
- { match: (p) => normPath(p).includes(".idx/") && p.endsWith(".mdc"), type: "IDX_CONFIG", label: "Firebase Rules" },
2893
- { match: (p) => normPath(p).includes(".roo/rules/") && p.endsWith(".mdc"), type: "ROO_RULES", label: "Roo Rules" },
2894
- { match: (p) => normPath(p).includes(".amazonq/rules/") && /\.mdc?$/.test(p), type: "AMAZONQ_RULES", label: "Amazon Q Rules" },
2895
- { match: (p) => normPath(p).includes(".augment/rules/") && /\.mdc?$/.test(p), type: "AUGMENT_RULES", label: "Augment Rules" },
2896
- { match: (p) => normPath(p).includes(".kilocode/rules/") && p.endsWith(".mdc"), type: "KILOCODE_RULES", label: "Kilo Code Rules" },
2897
- { match: (p) => normPath(p).includes(".kiro/steering/") && /\.mdc?$/.test(p), type: "KIRO_STEERING", label: "Kiro Steering" },
2898
- // Path-based configs
2899
- { match: (p) => normPath(p).includes(".github/copilot-instructions.md"), type: "COPILOT_INSTRUCTIONS", label: "Copilot Instructions" },
2900
- { match: (p) => normPath(p).includes(".zed/instructions.md"), type: "ZED_INSTRUCTIONS", label: "Zed Instructions" },
2901
- { match: (p) => normPath(p).includes(".openhands/microagents/repo.md"), type: "OPENHANDS_MD", label: "OpenHands Config" },
2902
- { match: (p) => normPath(p).includes(".junie/guidelines.md"), type: "JUNIE_GUIDELINES", label: "Junie Guidelines" },
2903
- { match: (p) => normPath(p).includes(".void/config.json"), type: "VOID_CONFIG", label: "Void Config" },
2904
- { match: (p) => normPath(p).includes(".continue/config.json"), type: "CONTINUE_CONFIG", label: "Continue Config" },
2905
- { match: (p) => normPath(p).includes(".cody/config.json"), type: "CODY_CONFIG", label: "Cody Config" },
2906
- { match: (p) => normPath(p).includes(".supermaven/config.json"), type: "CUSTOM", label: "Supermaven Config" },
2907
- { match: (p) => normPath(p).includes(".codegpt/config.json"), type: "CUSTOM", label: "CodeGPT Config" },
2908
- // Basename rules
2909
- { match: (p) => path2.basename(p) === "AGENTS.md", type: "AGENTS_MD", label: "AGENTS.md" },
2910
- { match: (p) => path2.basename(p) === "CLAUDE.md", type: "CLAUDE_MD", label: "CLAUDE.md" },
2911
- { match: (p) => path2.basename(p) === "AIDER.md", type: "AIDER_MD", label: "AIDER.md" },
2912
- { match: (p) => path2.basename(p) === "GEMINI.md", type: "GEMINI_MD", label: "GEMINI.md" },
2913
- { match: (p) => path2.basename(p) === "WARP.md", type: "WARP_MD", label: "WARP.md" },
2914
- { match: (p) => path2.basename(p) === "CRUSH.md", type: "CUSTOM", label: "CRUSH.md" },
2915
- { match: (p) => path2.basename(p) === ".windsurfrules", type: "WINDSURF_RULES", label: "Windsurf Rules" },
2916
- { match: (p) => path2.basename(p) === ".cursorrules", type: "CURSORRULES", label: "Cursor Rules (legacy)" },
2917
- { match: (p) => path2.basename(p) === ".clinerules", type: "CLINE_RULES", label: "Cline Rules" },
2918
- { match: (p) => path2.basename(p) === ".goosehints", type: "GOOSEHINTS", label: "Goose Hints" },
2919
- { match: (p) => path2.basename(p) === ".tabnine.yaml", type: "TABNINE_CONFIG", label: "Tabnine Config" },
2920
- { match: (p) => path2.basename(p) === "opencode.json", type: "OPENCODE_JSON", label: "OpenCode Config" },
2921
- { match: (p) => path2.basename(p) === "firebender.json", type: "CUSTOM", label: "Firebender Config" }
2922
- ];
2923
- function matchConfigFile(relativePath) {
2924
- for (const pattern of CONFIG_PATTERNS) {
2925
- if (pattern.match(relativePath)) {
2926
- return { type: pattern.type, label: pattern.label };
2927
- }
2928
- }
2929
- return null;
2930
- }
2931
3458
  function scanForAgentFiles(cwd, maxDepth = 5) {
2932
3459
  const results = [];
2933
3460
  function scan(dir, depth) {
@@ -3143,11 +3670,26 @@ async function pushCommand(fileArg, options) {
3143
3670
  const filename = path2.basename(file);
3144
3671
  const linked = await findBlueprintByFile(cwd, file);
3145
3672
  if (linked) {
3673
+ const refusal = refusePush(cwd, linked, options);
3674
+ if (refusal) {
3675
+ console.log(chalk6.yellow(`\u26A0 Not pushed: ${refusal}`));
3676
+ process.exit(1);
3677
+ }
3146
3678
  await updateBlueprint(cwd, file, linked.id, content, options, linked.checksum);
3147
3679
  } else {
3148
3680
  await createOrLinkBlueprint(cwd, file, filename, content, options, fileArg !== void 0);
3149
3681
  }
3150
3682
  }
3683
+ function refusePush(cwd, tracked, options) {
3684
+ if (options.force) return null;
3685
+ if (fs.existsSync(conflictFilePath(cwd, tracked.file))) {
3686
+ return `${tracked.file} has an unresolved sync conflict. Merge ${tracked.file}.remote into it and delete ${tracked.file}.remote first, or use --force.`;
3687
+ }
3688
+ if (tracked.variables) {
3689
+ return `${tracked.file} was pulled with --vars, so pushing it would replace the blueprint's variables with your values. Use --force to push it anyway.`;
3690
+ }
3691
+ return null;
3692
+ }
3151
3693
  async function updateBlueprint(cwd, file, blueprintId, content, options, expectedChecksum) {
3152
3694
  console.log(chalk6.cyan(`
3153
3695
  \u{1F4E4} Updating blueprint ${chalk6.bold(blueprintId)}...`));
@@ -3438,6 +3980,11 @@ async function pushHierarchy(cwd, files, options) {
3438
3980
  const content = fs.readFileSync(file.absolutePath, "utf-8");
3439
3981
  const tracked = await findBlueprintByFile(cwd, file.path);
3440
3982
  if (tracked) {
3983
+ const refusal = refusePush(cwd, tracked, options);
3984
+ if (refusal) {
3985
+ spinner.warn(`${file.path} \u2192 skipped: ${refusal}`);
3986
+ continue;
3987
+ }
3441
3988
  const updateData = { content };
3442
3989
  if (tracked.checksum && !options.force) {
3443
3990
  updateData.expected_checksum = tracked.checksum;
@@ -4674,6 +5221,7 @@ var COMMAND_DIRECTORIES = [
4674
5221
  { directory: ".cursor/commands", type: "cursor-command", platform: "cursor", templateType: "CURSOR_COMMAND" },
4675
5222
  { directory: ".claude/commands", type: "claude-command", platform: "claude", templateType: "CLAUDE_COMMAND" },
4676
5223
  { directory: ".windsurf/workflows", type: "windsurf-workflow", platform: "windsurf", templateType: "WINDSURF_WORKFLOW" },
5224
+ { directory: ".github/prompts", type: "copilot-prompt", platform: "copilot", templateType: "COPILOT_PROMPT" },
4677
5225
  { directory: ".copilot/prompts", type: "copilot-prompt", platform: "copilot", templateType: "COPILOT_PROMPT" },
4678
5226
  { directory: ".continue/prompts", type: "continue-prompt", platform: "continue", templateType: "CONTINUE_PROMPT" },
4679
5227
  { directory: ".opencode/commands", type: "opencode-command", platform: "opencode", templateType: "OPENCODE_COMMAND" }
@@ -4691,7 +5239,7 @@ async function detectCommandFiles(cwd) {
4691
5239
  const filePath = join2(dirPath, entry.name);
4692
5240
  try {
4693
5241
  const content = await readFileAsync(filePath, "utf-8");
4694
- const name = entry.name.replace(/\.md$/, "");
5242
+ const name = entry.name.replace(/(\.prompt)?\.md$/, "");
4695
5243
  commands.push({
4696
5244
  path: filePath,
4697
5245
  name,
@@ -6104,7 +6652,7 @@ var TEST_FRAMEWORKS2 = TEST_FRAMEWORKS;
6104
6652
 
6105
6653
  // src/commands/wizard.ts
6106
6654
  var DRAFTS_DIR = ".lynxprompt/drafts";
6107
- var CLI_VERSION = "2.1.38";
6655
+ var CLI_VERSION = "2.1.40";
6108
6656
  async function saveDraftLocally(name, config2, stepReached) {
6109
6657
  const draftsPath = join3(process.cwd(), DRAFTS_DIR);
6110
6658
  await mkdir3(draftsPath, { recursive: true });
@@ -6208,43 +6756,7 @@ var WIZARD_STEPS = [
6208
6756
  { id: "static", title: "Static Files", icon: "\u{1F4C4}" },
6209
6757
  { id: "extra", title: "Final Details", icon: "\u{1F4AC}" }
6210
6758
  ];
6211
- var ALL_PLATFORMS = [
6212
- // Popular platforms
6213
- { id: "universal", name: "Universal (AGENTS.md)", file: "AGENTS.md", icon: "\u{1F310}", note: "Works with Claude Code, Copilot, Aider, and many others" },
6214
- { id: "cursor", name: "Cursor", file: ".cursor/rules/", icon: "\u26A1", note: "AI-powered code editor with native rules" },
6215
- { id: "claude", name: "Claude Code", file: "CLAUDE.md", icon: "\u{1F9E0}", note: "Anthropic's agentic coding tool" },
6216
- { id: "copilot", name: "GitHub Copilot", file: ".github/copilot-instructions.md", icon: "\u{1F419}", note: "GitHub's AI pair programmer" },
6217
- { id: "windsurf", name: "Windsurf", file: ".windsurfrules", icon: "\u{1F3C4}", note: "Codeium's AI-native IDE" },
6218
- // AI IDEs
6219
- { id: "antigravity", name: "Antigravity", file: "GEMINI.md", icon: "\u{1F48E}", note: "Google's Gemini-powered IDE" },
6220
- { id: "zed", name: "Zed", file: ".zed/instructions.md", icon: "\u26A1", note: "High-performance editor with AI" },
6221
- { id: "void", name: "Void", file: ".void/config.json", icon: "\u{1F573}\uFE0F", note: "Open-source Cursor alternative" },
6222
- { id: "trae", name: "Trae AI", file: ".trae/rules/", icon: "\u{1F537}", note: "ByteDance's AI IDE" },
6223
- { id: "firebase", name: "Firebase Studio", file: ".idx/", icon: "\u{1F525}", note: "Google's cloud IDE" },
6224
- // Editor extensions
6225
- { id: "cline", name: "Cline", file: ".clinerules", icon: "\u{1F527}", note: "Autonomous coding agent for VS Code" },
6226
- { id: "roocode", name: "Roo Code", file: ".roo/rules/", icon: "\u{1F998}", note: "AI coding assistant for VS Code" },
6227
- { id: "continue", name: "Continue", file: ".continue/config.json", icon: "\u27A1\uFE0F", note: "Open-source AI autopilot" },
6228
- { id: "cody", name: "Sourcegraph Cody", file: ".cody/config.json", icon: "\u{1F50D}", note: "Context-aware AI assistant" },
6229
- { id: "tabnine", name: "Tabnine", file: ".tabnine.yaml", icon: "\u{1F4DD}", note: "AI code completion" },
6230
- { id: "supermaven", name: "Supermaven", file: ".supermaven/config.json", icon: "\u{1F9B8}", note: "Fast AI code completions" },
6231
- { id: "codegpt", name: "CodeGPT", file: ".codegpt/config.json", icon: "\u{1F4AC}", note: "VS Code AI assistant" },
6232
- { id: "amazonq", name: "Amazon Q", file: ".amazonq/rules/", icon: "\u{1F4E6}", note: "AWS AI coding companion" },
6233
- { id: "augment", name: "Augment Code", file: ".augment/rules/", icon: "\u{1F52E}", note: "AI code augmentation" },
6234
- { id: "kilocode", name: "Kilo Code", file: ".kilocode/rules/", icon: "\u{1F4CA}", note: "AI code generation" },
6235
- { id: "junie", name: "Junie", file: ".junie/guidelines.md", icon: "\u{1F3AF}", note: "JetBrains AI assistant" },
6236
- { id: "kiro", name: "Kiro", file: ".kiro/steering/", icon: "\u{1F680}", note: "AWS spec-driven agent" },
6237
- // CLI tools
6238
- { id: "aider", name: "Aider", file: "AIDER.md", icon: "\u{1F916}", note: "AI pair programming in terminal" },
6239
- { id: "goose", name: "Goose", file: ".goosehints", icon: "\u{1FABF}", note: "Block's AI coding agent" },
6240
- { id: "warp", name: "Warp AI", file: "WARP.md", icon: "\u{1F680}", note: "AI-powered terminal" },
6241
- { id: "gemini-cli", name: "Gemini CLI", file: "GEMINI.md", icon: "\u{1F48E}", note: "Google's Gemini in terminal" },
6242
- { id: "opencode", name: "Open Code", file: "opencode.json", icon: "\u{1F513}", note: "Open-source AI coding" },
6243
- // Other
6244
- { id: "openhands", name: "OpenHands", file: ".openhands/microagents/repo.md", icon: "\u{1F932}", note: "Open-source AI agent" },
6245
- { id: "crush", name: "Crush", file: "CRUSH.md", icon: "\u{1F4A5}", note: "AI coding assistant" },
6246
- { id: "firebender", name: "Firebender", file: "firebender.json", icon: "\u{1F525}", note: "AI code transformation" }
6247
- ];
6759
+ var ALL_PLATFORMS = PLATFORMS.filter((p) => !p.isCommand);
6248
6760
  var LANGUAGES3 = LANGUAGES2;
6249
6761
  var FRAMEWORKS3 = FRAMEWORKS2;
6250
6762
  var DATABASES3 = DATABASES2;
@@ -6971,10 +7483,10 @@ var promptConfig = {
6971
7483
  };
6972
7484
  function parsePlatformList(value) {
6973
7485
  const platforms = value.split(",").map((f) => f.trim()).filter(Boolean);
6974
- const unknown = platforms.filter((p) => !PLATFORM_FILES[p]);
7486
+ const unknown = platforms.filter((p) => !PLATFORM_FILES2[p]);
6975
7487
  if (unknown.length > 0 || platforms.length === 0) {
6976
7488
  console.error(chalk7.red(` \u2717 Unknown format: ${unknown.join(", ") || value}`));
6977
- console.error(chalk7.gray(` Supported formats: ${Object.keys(PLATFORM_FILES).join(", ")}`));
7489
+ console.error(chalk7.gray(` Supported formats: ${Object.keys(PLATFORM_FILES2).join(", ")}`));
6978
7490
  process.exit(1);
6979
7491
  }
6980
7492
  return platforms;
@@ -7287,7 +7799,7 @@ async function runWizardWithDraftProtection(options) {
7287
7799
  if (config2.enableAutoUpdate && api) {
7288
7800
  spinner.text = "Saving blueprint to cloud...";
7289
7801
  try {
7290
- const primaryFile = config2.primarySyncFormat ? PLATFORM_FILES[config2.primarySyncFormat] : void 0;
7802
+ const primaryFile = config2.primarySyncFormat ? PLATFORM_FILES2[config2.primarySyncFormat] : void 0;
7291
7803
  const mainFile = primaryFile && files[primaryFile] !== void 0 ? [primaryFile, files[primaryFile]] : Object.entries(files)[0];
7292
7804
  if (mainFile) {
7293
7805
  const [fileName, content] = mainFile;
@@ -9578,10 +10090,10 @@ var CONFIG_FILES = [
9578
10090
  { path: "CLAUDE.md", name: "CLAUDE.md", platform: "Claude Code" },
9579
10091
  { path: ".github/copilot-instructions.md", name: "Copilot Instructions", platform: "GitHub Copilot" },
9580
10092
  { path: ".windsurfrules", name: ".windsurfrules", platform: "Windsurf" },
9581
- { path: ".zed/instructions.md", name: "Zed Instructions", platform: "Zed" },
10093
+ { path: ".rules", name: ".rules", platform: "Zed" },
9582
10094
  { path: ".clinerules", name: ".clinerules", platform: "Cline" },
9583
10095
  { path: ".goosehints", name: ".goosehints", platform: "Goose" },
9584
- { path: "AIDER.md", name: "AIDER.md", platform: "Aider" }
10096
+ { path: "CONVENTIONS.md", name: "CONVENTIONS.md", platform: "Aider" }
9585
10097
  ];
9586
10098
  var CONFIG_DIRS = [
9587
10099
  { path: ".cursor/rules", name: ".cursor/rules/", platform: "Cursor" },
@@ -9655,6 +10167,8 @@ async function statusCommand() {
9655
10167
  console.log();
9656
10168
  }
9657
10169
  }
10170
+ console.log(chalk9.gray(" \u2713 means no local edits. Run 'lynxp sync --dry-run' to check the cloud too."));
10171
+ console.log();
9658
10172
  }
9659
10173
  console.log(chalk9.cyan("\u{1F4C4} AI Config Files"));
9660
10174
  console.log();
@@ -9756,7 +10270,10 @@ var CONFIG_FILES2 = [
9756
10270
  { path: ".clinerules", name: "Cline" },
9757
10271
  // may also be a directory of rule files
9758
10272
  { path: ".goosehints", name: "Goose" },
10273
+ { path: ".rules", name: "Zed" },
9759
10274
  { path: ".zed/instructions.md", name: "Zed" },
10275
+ { path: "CONVENTIONS.md", name: "Aider" },
10276
+ { path: ".idx/airules.md", name: "Firebase Studio" },
9760
10277
  { path: ".junie/guidelines.md", name: "Junie" },
9761
10278
  { path: ".openhands/microagents/repo.md", name: "OpenHands" }
9762
10279
  ];
@@ -10101,7 +10618,7 @@ async function diffCommand(fileOrId, options = {}) {
10101
10618
  if (fileOrId) {
10102
10619
  const tracked = await findBlueprintByFile(cwd, fileOrId);
10103
10620
  if (tracked) {
10104
- await diffFileWithBlueprint(cwd, fileOrId, tracked.id);
10621
+ await diffFileWithBlueprint(cwd, fileOrId, tracked.id, false, tracked.variables);
10105
10622
  return;
10106
10623
  }
10107
10624
  await diffWithBlueprintId(cwd, fileOrId);
@@ -10127,7 +10644,7 @@ async function diffCommand(fileOrId, options = {}) {
10127
10644
  }
10128
10645
  console.log(chalk12.cyan(`\u{1F4C4} ${blueprint.file}`));
10129
10646
  console.log(chalk12.gray(` Linked to: ${blueprint.name} (${blueprint.id})`));
10130
- const result = await diffFileWithBlueprint(cwd, blueprint.file, blueprint.id, true);
10647
+ const result = await diffFileWithBlueprint(cwd, blueprint.file, blueprint.id, true, blueprint.variables);
10131
10648
  if (result === "changed") {
10132
10649
  hasChanges = true;
10133
10650
  } else if (result === "same") {
@@ -10142,12 +10659,11 @@ async function diffCommand(fileOrId, options = {}) {
10142
10659
  } else if (!hasChanges) {
10143
10660
  console.log(chalk12.green("\u2713 All tracked files are in sync with their cloud blueprints!"));
10144
10661
  } else {
10145
- console.log(chalk12.gray("To push local changes: lynxp push"));
10146
- console.log(chalk12.gray("To pull cloud changes: lynxp pull <id>"));
10662
+ console.log(chalk12.gray("To bring both sides together: lynxp sync"));
10147
10663
  }
10148
10664
  console.log();
10149
10665
  }
10150
- async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false) {
10666
+ async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false, variables) {
10151
10667
  const filePath = join6(cwd, file);
10152
10668
  if (!existsSync4(filePath)) {
10153
10669
  console.log(chalk12.red(`\u2717 File not found: ${file}`));
@@ -10162,7 +10678,8 @@ async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false) {
10162
10678
  return "error";
10163
10679
  }
10164
10680
  const localContent = await readFile7(filePath, "utf-8");
10165
- const diff = computeDiff(blueprint.content, localContent);
10681
+ const cloudContent = variables ? fillBlueprintVariables(blueprint.content, variables).content : blueprint.content;
10682
+ const diff = computeDiff(cloudContent, localContent);
10166
10683
  const stats = getDiffStats(diff);
10167
10684
  if (stats.added === 0 && stats.removed === 0) {
10168
10685
  if (!compact) {
@@ -10329,12 +10846,162 @@ async function diffLocal(cwd) {
10329
10846
  console.log();
10330
10847
  }
10331
10848
 
10332
- // src/commands/link.ts
10849
+ // src/commands/sync.ts
10333
10850
  import chalk13 from "chalk";
10851
+ import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
10852
+ import { existsSync as existsSync5 } from "fs";
10853
+ import { dirname as dirname4, join as join7 } from "path";
10854
+ async function syncCommand(fileArg, options = {}) {
10855
+ const cwd = process.cwd();
10856
+ if (!isAuthenticated()) {
10857
+ console.log(chalk13.yellow("Not logged in. Run 'lynxp login' to authenticate."));
10858
+ process.exit(1);
10859
+ }
10860
+ const { blueprints } = await loadBlueprints(cwd);
10861
+ const targets = fileArg ? blueprints.filter((b) => toTrackedPath(cwd, b.file) === toTrackedPath(cwd, fileArg)) : blueprints;
10862
+ if (targets.length === 0) {
10863
+ console.log(
10864
+ chalk13.yellow(fileArg ? `${fileArg} is not tracked.` : "No tracked blueprints found.")
10865
+ );
10866
+ console.log(chalk13.gray("Track a file with 'lynxp pull <id>' or 'lynxp link <file> <id>'."));
10867
+ if (fileArg) process.exit(1);
10868
+ return;
10869
+ }
10870
+ console.log();
10871
+ console.log(chalk13.cyan(options.dryRun ? "\u{1F431} LynxPrompt Sync (dry run)" : "\u{1F431} LynxPrompt Sync"));
10872
+ console.log();
10873
+ const counts = {
10874
+ "in-sync": 0,
10875
+ pulled: 0,
10876
+ pushed: 0,
10877
+ kept: 0,
10878
+ conflict: 0,
10879
+ error: 0
10880
+ };
10881
+ for (const tracked of targets) {
10882
+ counts[await syncOne(cwd, tracked, options)]++;
10883
+ }
10884
+ console.log();
10885
+ const parts = [
10886
+ `${counts.pulled} pulled`,
10887
+ `${counts.pushed} pushed`,
10888
+ `${counts["in-sync"]} in sync`
10889
+ ];
10890
+ if (counts.kept) parts.push(`${counts.kept} local only`);
10891
+ if (counts.conflict) parts.push(`${counts.conflict} conflict`);
10892
+ if (counts.error) parts.push(`${counts.error} failed`);
10893
+ console.log(chalk13.gray(`${options.dryRun ? "Would be: " : ""}${parts.join(", ")}`));
10894
+ console.log();
10895
+ if (counts.conflict || counts.error) process.exitCode = 1;
10896
+ }
10897
+ async function syncOne(cwd, tracked, options) {
10898
+ const file = tracked.file;
10899
+ const filePath = join7(cwd, file);
10900
+ const remoteFile = conflictFilePath(cwd, file);
10901
+ const dry = options.dryRun === true;
10902
+ if (existsSync5(remoteFile)) {
10903
+ console.log(chalk13.red(`\u2717 ${file}: unresolved conflict`));
10904
+ console.log(chalk13.gray(` Merge ${file}.remote into ${file}, delete ${file}.remote, then run 'lynxp sync' again.`));
10905
+ return "conflict";
10906
+ }
10907
+ if (!existsSync5(filePath)) {
10908
+ console.log(chalk13.red(`\u2717 ${file}: file not found`));
10909
+ console.log(chalk13.gray(` Run 'lynxp pull ${tracked.id}' to restore it or 'lynxp unlink ${file}' to stop tracking it.`));
10910
+ return "error";
10911
+ }
10912
+ let remote;
10913
+ try {
10914
+ const { blueprint } = await api.getBlueprint(tracked.id);
10915
+ if (!blueprint?.content) {
10916
+ console.log(chalk13.red(`\u2717 ${file}: the cloud blueprint ${tracked.id} has no content`));
10917
+ return "error";
10918
+ }
10919
+ remote = blueprint.content;
10920
+ } catch (error) {
10921
+ console.log(chalk13.red(`\u2717 ${file}: could not fetch ${tracked.id}: ${errorText(error)}`));
10922
+ return "error";
10923
+ }
10924
+ const local = await readFile8(filePath, "utf-8");
10925
+ const rendered = tracked.variables ? fillBlueprintVariables(remote, tracked.variables).content : remote;
10926
+ const localChanged = calculateChecksum(local) !== (tracked.localChecksum ?? tracked.checksum);
10927
+ const remoteChanged = calculateChecksum(remote) !== tracked.checksum;
10928
+ if (calculateChecksum(local) === calculateChecksum(rendered)) {
10929
+ if (!dry && (localChanged || remoteChanged)) await record(cwd, tracked, remote, local);
10930
+ console.log(chalk13.green(`\u2713 ${file}: in sync`));
10931
+ return "in-sync";
10932
+ }
10933
+ if (!localChanged) {
10934
+ if (!dry) {
10935
+ await writeFile4(filePath, rendered, "utf-8");
10936
+ await record(cwd, tracked, remote, rendered);
10937
+ }
10938
+ console.log(chalk13.cyan(`\u2193 ${file}: ${dry ? "would update from the cloud" : "updated from the cloud"}`));
10939
+ return "pulled";
10940
+ }
10941
+ if (!remoteChanged) {
10942
+ const reason = tracked.variables ? "it was pulled with --vars, so pushing it would replace the blueprint's variables with your values" : !tracked.editable ? "marketplace blueprints are read-only" : null;
10943
+ if (reason) {
10944
+ console.log(chalk13.yellow(`\u25CF ${file}: local changes kept, not pushed (${reason})`));
10945
+ return "kept";
10946
+ }
10947
+ if (dry) {
10948
+ console.log(chalk13.cyan(`\u2191 ${file}: would push local changes`));
10949
+ return "pushed";
10950
+ }
10951
+ try {
10952
+ await api.updateBlueprint(tracked.id, { content: local, expected_checksum: tracked.checksum });
10953
+ await updateChecksum(cwd, file, local);
10954
+ console.log(chalk13.cyan(`\u2191 ${file}: pushed local changes`));
10955
+ return "pushed";
10956
+ } catch (error) {
10957
+ if (error instanceof ApiRequestError && error.statusCode === 409) {
10958
+ console.log(chalk13.red(`\u2717 ${file}: the cloud changed while syncing; run 'lynxp sync' again`));
10959
+ } else {
10960
+ console.log(chalk13.red(`\u2717 ${file}: push failed: ${errorText(error)}`));
10961
+ console.log(chalk13.gray(` Try 'lynxp push ${file}' for the full prompts.`));
10962
+ }
10963
+ return "error";
10964
+ }
10965
+ }
10966
+ if (!dry) {
10967
+ await mkdir4(dirname4(remoteFile), { recursive: true });
10968
+ await writeFile4(remoteFile, rendered, "utf-8");
10969
+ await record(cwd, tracked, remote, rendered);
10970
+ }
10971
+ console.log(chalk13.red(`\u2717 ${file}: changed both locally and in the cloud`));
10972
+ console.log(
10973
+ chalk13.gray(
10974
+ dry ? ` Sync would write the cloud version to ${file}.remote and leave ${file} as it is.` : ` Cloud version written to ${file}.remote; ${file} was not touched.
10975
+ Merge what you need into ${file}, delete ${file}.remote, then run 'lynxp sync' again.`
10976
+ )
10977
+ );
10978
+ return "conflict";
10979
+ }
10980
+ async function record(cwd, t, remote, written) {
10981
+ await trackBlueprint(cwd, {
10982
+ id: t.id,
10983
+ name: t.name,
10984
+ file: t.file,
10985
+ content: remote,
10986
+ source: t.source,
10987
+ version: t.version,
10988
+ hierarchyId: t.hierarchyId,
10989
+ hierarchyName: t.hierarchyName,
10990
+ repositoryPath: t.repositoryPath,
10991
+ variables: t.variables,
10992
+ localContent: t.variables ? written : void 0
10993
+ });
10994
+ }
10995
+ function errorText(error) {
10996
+ return error instanceof Error ? error.message : String(error);
10997
+ }
10998
+
10999
+ // src/commands/link.ts
11000
+ import chalk14 from "chalk";
10334
11001
  import ora10 from "ora";
10335
11002
  import prompts4 from "prompts";
10336
- import { join as join7 } from "path";
10337
- import { existsSync as existsSync5 } from "fs";
11003
+ import { join as join8 } from "path";
11004
+ import { existsSync as existsSync6 } from "fs";
10338
11005
  function getSourceFromVisibility2(visibility) {
10339
11006
  switch (visibility) {
10340
11007
  case "PUBLIC":
@@ -10354,7 +11021,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10354
11021
  return;
10355
11022
  }
10356
11023
  console.log();
10357
- console.log(chalk13.cyan("\u{1F431} Link File to Blueprint"));
11024
+ console.log(chalk14.cyan("\u{1F431} Link File to Blueprint"));
10358
11025
  console.log();
10359
11026
  let file;
10360
11027
  let blueprintId = blueprintIdArg;
@@ -10365,16 +11032,16 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10365
11032
  ".cursor/rules/project.mdc",
10366
11033
  ".github/copilot-instructions.md",
10367
11034
  ".windsurfrules",
10368
- ".zed/instructions.md",
11035
+ ".rules",
10369
11036
  ".clinerules"
10370
11037
  ];
10371
- const foundFiles = configFiles.filter((f) => existsSync5(join7(cwd, f)));
11038
+ const foundFiles = configFiles.filter((f) => existsSync6(join8(cwd, f)));
10372
11039
  if (foundFiles.length === 0) {
10373
- console.log(chalk13.yellow("No AI configuration files found in this directory."));
11040
+ console.log(chalk14.yellow("No AI configuration files found in this directory."));
10374
11041
  console.log();
10375
- console.log(chalk13.gray("Create one first:"));
10376
- console.log(chalk13.gray(" lynxp wizard Generate a new config file"));
10377
- console.log(chalk13.gray(" lynxp pull <id> Download from marketplace"));
11042
+ console.log(chalk14.gray("Create one first:"));
11043
+ console.log(chalk14.gray(" lynxp wizard Generate a new config file"));
11044
+ console.log(chalk14.gray(" lynxp pull <id> Download from marketplace"));
10378
11045
  return;
10379
11046
  }
10380
11047
  const { selectedFile } = await prompts4({
@@ -10388,22 +11055,22 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10388
11055
  }))
10389
11056
  });
10390
11057
  if (!selectedFile) {
10391
- console.log(chalk13.gray("Cancelled."));
11058
+ console.log(chalk14.gray("Cancelled."));
10392
11059
  return;
10393
11060
  }
10394
11061
  file = selectedFile;
10395
11062
  } else {
10396
11063
  file = fileArg;
10397
11064
  }
10398
- const filePath = join7(cwd, file);
10399
- if (!existsSync5(filePath)) {
10400
- console.log(chalk13.red(`\u2717 File not found: ${file}`));
11065
+ const filePath = join8(cwd, file);
11066
+ if (!existsSync6(filePath)) {
11067
+ console.log(chalk14.red(`\u2717 File not found: ${file}`));
10401
11068
  return;
10402
11069
  }
10403
11070
  const existing = await findBlueprintByFile(cwd, file);
10404
11071
  if (existing) {
10405
- console.log(chalk13.yellow(`This file is already linked to: ${existing.name}`));
10406
- console.log(chalk13.gray(` ID: ${existing.id}`));
11072
+ console.log(chalk14.yellow(`This file is already linked to: ${existing.name}`));
11073
+ console.log(chalk14.gray(` ID: ${existing.id}`));
10407
11074
  console.log();
10408
11075
  const { proceed } = await prompts4({
10409
11076
  type: "confirm",
@@ -10412,13 +11079,13 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10412
11079
  initial: false
10413
11080
  });
10414
11081
  if (!proceed) {
10415
- console.log(chalk13.gray("Cancelled."));
11082
+ console.log(chalk14.gray("Cancelled."));
10416
11083
  return;
10417
11084
  }
10418
11085
  }
10419
11086
  if (!blueprintId) {
10420
11087
  if (!isAuthenticated()) {
10421
- console.log(chalk13.yellow("You need to login to access your blueprints."));
11088
+ console.log(chalk14.yellow("You need to login to access your blueprints."));
10422
11089
  const { doLogin } = await prompts4({
10423
11090
  type: "confirm",
10424
11091
  name: "doLogin",
@@ -10426,10 +11093,10 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10426
11093
  initial: true
10427
11094
  });
10428
11095
  if (doLogin) {
10429
- console.log(chalk13.gray("Run 'lynxp login' in another terminal, then come back here."));
11096
+ console.log(chalk14.gray("Run 'lynxp login' in another terminal, then come back here."));
10430
11097
  return;
10431
11098
  }
10432
- console.log(chalk13.gray("Cancelled."));
11099
+ console.log(chalk14.gray("Cancelled."));
10433
11100
  return;
10434
11101
  }
10435
11102
  const { searchMethod } = await prompts4({
@@ -10443,7 +11110,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10443
11110
  ]
10444
11111
  });
10445
11112
  if (!searchMethod) {
10446
- console.log(chalk13.gray("Cancelled."));
11113
+ console.log(chalk14.gray("Cancelled."));
10447
11114
  return;
10448
11115
  }
10449
11116
  if (searchMethod === "list") {
@@ -10452,8 +11119,8 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10452
11119
  const blueprints = await api.listAllBlueprints();
10453
11120
  spinner2.stop();
10454
11121
  if (!blueprints || blueprints.length === 0) {
10455
- console.log(chalk13.yellow("You don't have any blueprints yet."));
10456
- console.log(chalk13.gray("Create one with 'lynxp push' or search the marketplace."));
11122
+ console.log(chalk14.yellow("You don't have any blueprints yet."));
11123
+ console.log(chalk14.gray("Create one with 'lynxp push' or search the marketplace."));
10457
11124
  return;
10458
11125
  }
10459
11126
  const { selected } = await prompts4({
@@ -10467,13 +11134,13 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10467
11134
  }))
10468
11135
  });
10469
11136
  if (!selected) {
10470
- console.log(chalk13.gray("Cancelled."));
11137
+ console.log(chalk14.gray("Cancelled."));
10471
11138
  return;
10472
11139
  }
10473
11140
  blueprintId = selected;
10474
11141
  } catch {
10475
11142
  spinner2.stop();
10476
- console.log(chalk13.red("\u2717 Could not fetch blueprints"));
11143
+ console.log(chalk14.red("\u2717 Could not fetch blueprints"));
10477
11144
  return;
10478
11145
  }
10479
11146
  } else if (searchMethod === "search") {
@@ -10483,7 +11150,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10483
11150
  message: "Search for:"
10484
11151
  });
10485
11152
  if (!query) {
10486
- console.log(chalk13.gray("Cancelled."));
11153
+ console.log(chalk14.gray("Cancelled."));
10487
11154
  return;
10488
11155
  }
10489
11156
  const spinner2 = ora10(`Searching for "${query}"...`).start();
@@ -10491,7 +11158,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10491
11158
  const results = await api.searchBlueprints(query, 10);
10492
11159
  spinner2.stop();
10493
11160
  if (!results.templates || results.templates.length === 0) {
10494
- console.log(chalk13.yellow(`No blueprints found for "${query}"`));
11161
+ console.log(chalk14.yellow(`No blueprints found for "${query}"`));
10495
11162
  return;
10496
11163
  }
10497
11164
  const { selected } = await prompts4({
@@ -10505,13 +11172,13 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10505
11172
  }))
10506
11173
  });
10507
11174
  if (!selected) {
10508
- console.log(chalk13.gray("Cancelled."));
11175
+ console.log(chalk14.gray("Cancelled."));
10509
11176
  return;
10510
11177
  }
10511
11178
  blueprintId = selected;
10512
11179
  } catch {
10513
11180
  spinner2.stop();
10514
- console.log(chalk13.red("\u2717 Search failed"));
11181
+ console.log(chalk14.red("\u2717 Search failed"));
10515
11182
  return;
10516
11183
  }
10517
11184
  } else {
@@ -10521,82 +11188,82 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
10521
11188
  message: "Enter blueprint ID:"
10522
11189
  });
10523
11190
  if (!manualId) {
10524
- console.log(chalk13.gray("Cancelled."));
11191
+ console.log(chalk14.gray("Cancelled."));
10525
11192
  return;
10526
11193
  }
10527
11194
  blueprintId = manualId;
10528
11195
  }
10529
11196
  }
10530
11197
  if (!blueprintId) {
10531
- console.log(chalk13.red("\u2717 No blueprint ID provided."));
11198
+ console.log(chalk14.red("\u2717 No blueprint ID provided."));
10532
11199
  return;
10533
11200
  }
10534
- const spinner = ora10(`Fetching blueprint ${chalk13.cyan(blueprintId)}...`).start();
11201
+ const spinner = ora10(`Fetching blueprint ${chalk14.cyan(blueprintId)}...`).start();
10535
11202
  try {
10536
11203
  const { blueprint } = await api.getBlueprint(blueprintId);
10537
11204
  spinner.stop();
10538
11205
  const source = getSourceFromVisibility2(blueprint.visibility);
10539
11206
  const isMarketplace = source === "marketplace";
10540
11207
  console.log();
10541
- console.log(chalk13.cyan(`\u{1F431} Blueprint: ${chalk13.bold(safeText(blueprint.name))}`));
11208
+ console.log(chalk14.cyan(`\u{1F431} Blueprint: ${chalk14.bold(safeText(blueprint.name))}`));
10542
11209
  if (blueprint.description) {
10543
- console.log(chalk13.gray(` ${safeText(blueprint.description)}`));
11210
+ console.log(chalk14.gray(` ${safeText(blueprint.description)}`));
10544
11211
  }
10545
- console.log(chalk13.gray(` Visibility: ${blueprint.visibility}`));
11212
+ console.log(chalk14.gray(` Visibility: ${blueprint.visibility}`));
10546
11213
  console.log();
10547
11214
  if (isMarketplace) {
10548
- console.log(chalk13.yellow("\u26A0 This is a marketplace blueprint."));
10549
- console.log(chalk13.gray(" Your local changes will NOT sync back to the cloud."));
10550
- console.log(chalk13.gray(" To make changes, you'll need to create your own copy."));
11215
+ console.log(chalk14.yellow("\u26A0 This is a marketplace blueprint."));
11216
+ console.log(chalk14.gray(" Your local changes will NOT sync back to the cloud."));
11217
+ console.log(chalk14.gray(" To make changes, you'll need to create your own copy."));
10551
11218
  console.log();
10552
11219
  }
10553
11220
  const { confirm } = await prompts4({
10554
11221
  type: "confirm",
10555
11222
  name: "confirm",
10556
- message: `Link ${chalk13.cyan(file)} to ${chalk13.cyan(blueprint.name)}?`,
11223
+ message: `Link ${chalk14.cyan(file)} to ${chalk14.cyan(blueprint.name)}?`,
10557
11224
  initial: true
10558
11225
  });
10559
11226
  if (!confirm) {
10560
- console.log(chalk13.gray("Cancelled."));
11227
+ console.log(chalk14.gray("Cancelled."));
10561
11228
  return;
10562
11229
  }
10563
11230
  await linkBlueprint(cwd, file, blueprint.id, blueprint.name, source, blueprint.content);
10564
11231
  console.log();
10565
- console.log(chalk13.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
11232
+ console.log(chalk14.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
10566
11233
  console.log();
10567
- console.log(chalk13.gray("Next steps:"));
10568
- console.log(chalk13.gray(` \u2022 Run 'lynxp diff' to see differences`));
10569
- console.log(chalk13.gray(` \u2022 Run 'lynxp status' to see all tracked blueprints`));
11234
+ console.log(chalk14.gray("Next steps:"));
11235
+ console.log(chalk14.gray(` \u2022 Run 'lynxp diff' to see differences`));
11236
+ console.log(chalk14.gray(` \u2022 Run 'lynxp status' to see all tracked blueprints`));
10570
11237
  if (!isMarketplace) {
10571
- console.log(chalk13.gray(` \u2022 Run 'lynxp push' to push local changes to cloud`));
11238
+ console.log(chalk14.gray(` \u2022 Run 'lynxp push' to push local changes to cloud`));
10572
11239
  }
10573
11240
  console.log();
10574
11241
  } catch (error) {
10575
11242
  spinner.stop();
10576
11243
  if (error instanceof ApiRequestError) {
10577
11244
  if (error.statusCode === 404) {
10578
- console.log(chalk13.red(`\u2717 Blueprint not found: ${blueprintId}`));
10579
- console.log(chalk13.gray(" Make sure the ID is correct. Use 'lynxp list' or 'lynxp search' to find blueprints."));
11245
+ console.log(chalk14.red(`\u2717 Blueprint not found: ${blueprintId}`));
11246
+ console.log(chalk14.gray(" Make sure the ID is correct. Use 'lynxp list' or 'lynxp search' to find blueprints."));
10580
11247
  } else if (error.statusCode === 403) {
10581
- console.log(chalk13.red("\u2717 You don't have access to this blueprint."));
11248
+ console.log(chalk14.red("\u2717 You don't have access to this blueprint."));
10582
11249
  } else {
10583
- console.log(chalk13.red(`\u2717 Error: ${error.message}`));
11250
+ console.log(chalk14.red(`\u2717 Error: ${error.message}`));
10584
11251
  }
10585
11252
  } else {
10586
- console.log(chalk13.red("\u2717 An unexpected error occurred."));
11253
+ console.log(chalk14.red("\u2717 An unexpected error occurred."));
10587
11254
  }
10588
11255
  }
10589
11256
  }
10590
11257
  async function unlinkCommand(fileArg) {
10591
11258
  const cwd = process.cwd();
10592
11259
  console.log();
10593
- console.log(chalk13.cyan("\u{1F431} Unlink File from Blueprint"));
11260
+ console.log(chalk14.cyan("\u{1F431} Unlink File from Blueprint"));
10594
11261
  console.log();
10595
11262
  let file;
10596
11263
  if (!fileArg) {
10597
11264
  const status = await checkSyncStatus(cwd);
10598
11265
  if (status.length === 0) {
10599
- console.log(chalk13.yellow("No files are currently linked to blueprints."));
11266
+ console.log(chalk14.yellow("No files are currently linked to blueprints."));
10600
11267
  return;
10601
11268
  }
10602
11269
  const { selectedFile } = await prompts4({
@@ -10610,7 +11277,7 @@ async function unlinkCommand(fileArg) {
10610
11277
  }))
10611
11278
  });
10612
11279
  if (!selectedFile) {
10613
- console.log(chalk13.gray("Cancelled."));
11280
+ console.log(chalk14.gray("Cancelled."));
10614
11281
  return;
10615
11282
  }
10616
11283
  file = selectedFile;
@@ -10619,77 +11286,77 @@ async function unlinkCommand(fileArg) {
10619
11286
  }
10620
11287
  const tracked = await findBlueprintByFile(cwd, file);
10621
11288
  if (!tracked) {
10622
- console.log(chalk13.yellow(`File is not linked to any blueprint: ${file}`));
11289
+ console.log(chalk14.yellow(`File is not linked to any blueprint: ${file}`));
10623
11290
  return;
10624
11291
  }
10625
- console.log(chalk13.gray(`Currently linked to: ${tracked.name}`));
10626
- console.log(chalk13.gray(` ID: ${tracked.id}`));
10627
- console.log(chalk13.gray(` Source: ${tracked.source}`));
11292
+ console.log(chalk14.gray(`Currently linked to: ${tracked.name}`));
11293
+ console.log(chalk14.gray(` ID: ${tracked.id}`));
11294
+ console.log(chalk14.gray(` Source: ${tracked.source}`));
10628
11295
  console.log();
10629
11296
  const { confirm } = await prompts4({
10630
11297
  type: "confirm",
10631
11298
  name: "confirm",
10632
- message: `Unlink ${chalk13.cyan(file)} from ${chalk13.cyan(tracked.name)}?`,
11299
+ message: `Unlink ${chalk14.cyan(file)} from ${chalk14.cyan(tracked.name)}?`,
10633
11300
  initial: true
10634
11301
  });
10635
11302
  if (!confirm) {
10636
- console.log(chalk13.gray("Cancelled."));
11303
+ console.log(chalk14.gray("Cancelled."));
10637
11304
  return;
10638
11305
  }
10639
11306
  const success = await untrackBlueprint(cwd, file);
10640
11307
  if (success) {
10641
11308
  console.log();
10642
- console.log(chalk13.green(`\u2705 Unlinked: ${file}`));
10643
- console.log(chalk13.gray(" The file is now standalone. Changes won't sync with the cloud."));
11309
+ console.log(chalk14.green(`\u2705 Unlinked: ${file}`));
11310
+ console.log(chalk14.gray(" The file is now standalone. Changes won't sync with the cloud."));
10644
11311
  console.log();
10645
11312
  } else {
10646
- console.log(chalk13.red("\u2717 Failed to unlink file."));
11313
+ console.log(chalk14.red("\u2717 Failed to unlink file."));
10647
11314
  }
10648
11315
  }
10649
11316
  async function listTrackedBlueprints(cwd) {
10650
11317
  console.log();
10651
- console.log(chalk13.cyan("\u{1F431} Tracked Blueprints"));
11318
+ console.log(chalk14.cyan("\u{1F431} Tracked Blueprints"));
10652
11319
  console.log();
10653
11320
  const status = await checkSyncStatus(cwd);
10654
11321
  if (status.length === 0) {
10655
- console.log(chalk13.gray("No blueprints are currently tracked."));
11322
+ console.log(chalk14.gray("No blueprints are currently tracked."));
10656
11323
  console.log();
10657
- console.log(chalk13.gray("To track a blueprint:"));
10658
- console.log(chalk13.gray(" lynxp pull <blueprint-id> Download and track a blueprint"));
10659
- console.log(chalk13.gray(" lynxp link Link an existing file to a blueprint"));
11324
+ console.log(chalk14.gray("To track a blueprint:"));
11325
+ console.log(chalk14.gray(" lynxp pull <blueprint-id> Download and track a blueprint"));
11326
+ console.log(chalk14.gray(" lynxp link Link an existing file to a blueprint"));
10660
11327
  return;
10661
11328
  }
10662
11329
  for (const { blueprint, localModified, fileExists: fileExists2 } of status) {
10663
- const statusIcon = !fileExists2 ? chalk13.red("\u2717") : localModified ? chalk13.yellow("\u25CF") : chalk13.green("\u2713");
11330
+ const statusIcon = !fileExists2 ? chalk14.red("\u2717") : localModified ? chalk14.yellow("\u25CF") : chalk14.green("\u2713");
10664
11331
  const sourceLabel = {
10665
- marketplace: chalk13.gray("[marketplace]"),
10666
- team: chalk13.blue("[team]"),
10667
- private: chalk13.green("[private]"),
10668
- local: chalk13.gray("[local]")
11332
+ marketplace: chalk14.gray("[marketplace]"),
11333
+ team: chalk14.blue("[team]"),
11334
+ private: chalk14.green("[private]"),
11335
+ local: chalk14.gray("[local]")
10669
11336
  }[blueprint.source];
10670
- console.log(`${statusIcon} ${chalk13.cyan(blueprint.file)}`);
11337
+ console.log(`${statusIcon} ${chalk14.cyan(blueprint.file)}`);
10671
11338
  console.log(` ${sourceLabel} ${blueprint.name}`);
10672
- console.log(` ${chalk13.gray(`ID: ${blueprint.id}`)}`);
11339
+ console.log(` ${chalk14.gray(`ID: ${blueprint.id}`)}`);
10673
11340
  if (!fileExists2) {
10674
- console.log(chalk13.red(` \u26A0 File not found`));
11341
+ console.log(chalk14.red(` \u26A0 File not found`));
10675
11342
  } else if (localModified) {
10676
- console.log(chalk13.yellow(` \u26A0 Local changes detected`));
11343
+ console.log(chalk14.yellow(` \u26A0 Local changes detected`));
10677
11344
  }
10678
11345
  console.log();
10679
11346
  }
10680
- console.log(chalk13.gray("Legend:"));
10681
- console.log(chalk13.gray(` ${chalk13.green("\u2713")} In sync ${chalk13.yellow("\u25CF")} Modified locally ${chalk13.red("\u2717")} Missing`));
11347
+ console.log(chalk14.gray("Legend:"));
11348
+ console.log(chalk14.gray(` ${chalk14.green("\u2713")} In sync ${chalk14.yellow("\u25CF")} Modified locally ${chalk14.red("\u2717")} Missing`));
10682
11349
  console.log();
10683
11350
  }
10684
11351
 
10685
11352
  // src/commands/analyze.ts
10686
- import chalk14 from "chalk";
11353
+ import chalk15 from "chalk";
10687
11354
  import ora11 from "ora";
10688
11355
  async function analyzeCommand(options) {
10689
11356
  console.log();
10690
11357
  if (!options.json) {
10691
- console.log(chalk14.cyan.bold(" \u{1F50D} LynxPrompt Analyzer"));
10692
- console.log(chalk14.gray(" Detect project configuration and tech stack"));
11358
+ console.log(chalk15.cyan.bold(" \u{1F50D} LynxPrompt Analyzer"));
11359
+ console.log(chalk15.gray(" Detect project configuration and tech stack"));
10693
11360
  console.log();
10694
11361
  }
10695
11362
  let detected;
@@ -10697,7 +11364,7 @@ async function analyzeCommand(options) {
10697
11364
  if (options.remote) {
10698
11365
  if (!isGitUrl(options.remote)) {
10699
11366
  if (!options.json) {
10700
- console.log(chalk14.red(" \u2717 Invalid Git URL provided"));
11367
+ console.log(chalk15.red(" \u2717 Invalid Git URL provided"));
10701
11368
  }
10702
11369
  process.exit(1);
10703
11370
  }
@@ -10710,7 +11377,7 @@ async function analyzeCommand(options) {
10710
11377
  spinner.succeed("Remote repository analyzed");
10711
11378
  } else {
10712
11379
  spinner.fail("Could not analyze repository");
10713
- console.log(chalk14.gray(" The repository may be private or inaccessible."));
11380
+ console.log(chalk15.gray(" The repository may be private or inaccessible."));
10714
11381
  process.exit(1);
10715
11382
  }
10716
11383
  } else {
@@ -10743,57 +11410,57 @@ async function analyzeCommand(options) {
10743
11410
  }
10744
11411
  console.log();
10745
11412
  if (!detected) {
10746
- console.log(chalk14.yellow(" No project configuration detected."));
11413
+ console.log(chalk15.yellow(" No project configuration detected."));
10747
11414
  console.log();
10748
- console.log(chalk14.gray(" Tips:"));
10749
- console.log(chalk14.gray(" \u2022 Make sure you're in a project directory with a package.json, pyproject.toml, etc."));
10750
- console.log(chalk14.gray(" \u2022 Use --remote <url> to analyze a remote repository"));
11415
+ console.log(chalk15.gray(" Tips:"));
11416
+ console.log(chalk15.gray(" \u2022 Make sure you're in a project directory with a package.json, pyproject.toml, etc."));
11417
+ console.log(chalk15.gray(" \u2022 Use --remote <url> to analyze a remote repository"));
10751
11418
  console.log();
10752
11419
  return;
10753
11420
  }
10754
- console.log(chalk14.green.bold(" \u{1F4CA} Project Analysis"));
11421
+ console.log(chalk15.green.bold(" \u{1F4CA} Project Analysis"));
10755
11422
  console.log();
10756
- console.log(chalk14.white(" Basic Info"));
10757
- console.log(chalk14.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10758
- console.log(` ${chalk14.dim("Name:")} ${chalk14.white(detected.name || "unknown")}`);
11423
+ console.log(chalk15.white(" Basic Info"));
11424
+ console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11425
+ console.log(` ${chalk15.dim("Name:")} ${chalk15.white(detected.name || "unknown")}`);
10759
11426
  if (detected.description) {
10760
- console.log(` ${chalk14.dim("Description:")} ${chalk14.gray(detected.description)}`);
11427
+ console.log(` ${chalk15.dim("Description:")} ${chalk15.gray(detected.description)}`);
10761
11428
  }
10762
- console.log(` ${chalk14.dim("Type:")} ${chalk14.white(detected.type || "application")}`);
11429
+ console.log(` ${chalk15.dim("Type:")} ${chalk15.white(detected.type || "application")}`);
10763
11430
  if (detected.license) {
10764
- console.log(` ${chalk14.dim("License:")} ${chalk14.white(detected.license.toUpperCase())}`);
11431
+ console.log(` ${chalk15.dim("License:")} ${chalk15.white(detected.license.toUpperCase())}`);
10765
11432
  }
10766
11433
  console.log();
10767
- console.log(chalk14.white(" Tech Stack"));
10768
- console.log(chalk14.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11434
+ console.log(chalk15.white(" Tech Stack"));
11435
+ console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10769
11436
  if (detected.stack.length > 0) {
10770
11437
  for (const tech of detected.stack) {
10771
- console.log(` ${chalk14.cyan("\u2022")} ${tech}`);
11438
+ console.log(` ${chalk15.cyan("\u2022")} ${tech}`);
10772
11439
  }
10773
11440
  } else {
10774
- console.log(chalk14.gray(" No tech stack detected"));
11441
+ console.log(chalk15.gray(" No tech stack detected"));
10775
11442
  }
10776
11443
  console.log();
10777
- console.log(chalk14.white(" Infrastructure"));
10778
- console.log(chalk14.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10779
- console.log(` ${chalk14.dim("Package Manager:")} ${detected.packageManager || chalk14.gray("none detected")}`);
10780
- console.log(` ${chalk14.dim("Repo Host:")} ${detected.repoHost || chalk14.gray("none detected")}`);
10781
- console.log(` ${chalk14.dim("CI/CD:")} ${detected.cicd || chalk14.gray("none detected")}`);
10782
- console.log(` ${chalk14.dim("Docker:")} ${detected.hasDocker ? chalk14.green("yes") : chalk14.gray("no")}`);
11444
+ console.log(chalk15.white(" Infrastructure"));
11445
+ console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11446
+ console.log(` ${chalk15.dim("Package Manager:")} ${detected.packageManager || chalk15.gray("none detected")}`);
11447
+ console.log(` ${chalk15.dim("Repo Host:")} ${detected.repoHost || chalk15.gray("none detected")}`);
11448
+ console.log(` ${chalk15.dim("CI/CD:")} ${detected.cicd || chalk15.gray("none detected")}`);
11449
+ console.log(` ${chalk15.dim("Docker:")} ${detected.hasDocker ? chalk15.green("yes") : chalk15.gray("no")}`);
10783
11450
  console.log();
10784
11451
  if (detected.commands && Object.keys(detected.commands).length > 0) {
10785
- console.log(chalk14.white(" Commands"));
10786
- console.log(chalk14.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11452
+ console.log(chalk15.white(" Commands"));
11453
+ console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10787
11454
  for (const [key, value] of Object.entries(detected.commands)) {
10788
11455
  if (value) {
10789
11456
  const cmdValue = Array.isArray(value) ? value.join(", ") : value;
10790
- console.log(` ${chalk14.dim(key.padEnd(10))} ${chalk14.yellow(cmdValue)}`);
11457
+ console.log(` ${chalk15.dim(key.padEnd(10))} ${chalk15.yellow(cmdValue)}`);
10791
11458
  }
10792
11459
  }
10793
11460
  console.log();
10794
11461
  }
10795
- console.log(chalk14.white(" \u{1F4A1} Recommendations"));
10796
- console.log(chalk14.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11462
+ console.log(chalk15.white(" \u{1F4A1} Recommendations"));
11463
+ console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
10797
11464
  const recommendations = [];
10798
11465
  if (!detected.cicd) {
10799
11466
  recommendations.push("Add CI/CD configuration (GitHub Actions, GitLab CI, etc.)");
@@ -10812,132 +11479,81 @@ async function analyzeCommand(options) {
10812
11479
  }
10813
11480
  if (recommendations.length > 0) {
10814
11481
  for (const rec of recommendations) {
10815
- console.log(` ${chalk14.yellow("\u2022")} ${rec}`);
11482
+ console.log(` ${chalk15.yellow("\u2022")} ${rec}`);
10816
11483
  }
10817
11484
  } else {
10818
- console.log(chalk14.green(" \u2713 Project looks well-configured!"));
11485
+ console.log(chalk15.green(" \u2713 Project looks well-configured!"));
10819
11486
  }
10820
11487
  console.log();
10821
- console.log(chalk14.gray(" Run 'lynxp wizard' to generate AI IDE configuration"));
11488
+ console.log(chalk15.gray(" Run 'lynxp wizard' to generate AI IDE configuration"));
10822
11489
  console.log();
10823
11490
  }
10824
11491
 
10825
11492
  // src/commands/convert.ts
10826
- import chalk15 from "chalk";
10827
- import { readFile as readFile8, writeFile as writeFile4, access as access5 } from "fs/promises";
10828
- import { join as join8, basename, resolve as resolve2, dirname as dirname4 } from "path";
11493
+ import chalk16 from "chalk";
11494
+ import { readFile as readFile9, writeFile as writeFile5, access as access6 } from "fs/promises";
11495
+ import { basename as basename2, resolve as resolve2, dirname as dirname5 } from "path";
10829
11496
  import ora12 from "ora";
10830
11497
 
10831
11498
  // src/utils/convert-format.ts
10832
- var isCommandTarget = (target) => target.includes("-command") || target.includes("-prompt") || target.includes("-workflow");
11499
+ import { access as access5 } from "fs/promises";
11500
+ import { join as join9 } from "path";
11501
+
11502
+ // ../packages/shared/src/rules-format.ts
11503
+ function yamlString(value) {
11504
+ return JSON.stringify(value);
11505
+ }
11506
+ var cursorFrontmatter = (description) => [`description: ${yamlString(description)}`, "alwaysApply: true"];
11507
+ var PLATFORM_FRONTMATTER = {
11508
+ cursor: cursorFrontmatter,
11509
+ firebender: cursorFrontmatter,
11510
+ // Firebender reads Cursor-style .mdc rules
11511
+ kiro: () => ["inclusion: always"],
11512
+ augment: (description) => ["type: always_apply", `description: ${yamlString(description)}`]
11513
+ };
11514
+ var FRONTMATTER_PATTERN = /^---\r?\n[\s\S]*?\r?\n---[ \t]*(?:\r?\n)+/;
11515
+ function frontmatterLines(platform2, description) {
11516
+ const keys = PLATFORM_FRONTMATTER[platform2];
11517
+ return keys ? ["---", ...keys(description), "---", ""] : [];
11518
+ }
10833
11519
  function splitFrontmatter(content) {
10834
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/);
10835
- if (!match) return { frontmatter: null, body: content };
10836
- return { frontmatter: match[1], body: content.slice(match[0].length).replace(/^\s*\n/, "") };
10837
- }
10838
- function parseMarkdownConfig(content) {
10839
- const config2 = {};
10840
- const sections = content.split(/^##\s+/m);
10841
- for (const section of sections) {
10842
- const lines = section.trim().split("\n");
10843
- if (lines.length === 0) continue;
10844
- const title = lines[0].toLowerCase().trim();
10845
- const body2 = lines.slice(1).join("\n").trim();
10846
- config2[title] = body2;
10847
- }
10848
- config2._raw = content;
11520
+ const match = content.match(FRONTMATTER_PATTERN);
11521
+ if (!match) return { frontmatter: "", body: content };
11522
+ return { frontmatter: match[0], body: content.slice(match[0].length) };
11523
+ }
11524
+ function adaptRulesContent(content, fromPlatform, toPlatform, description) {
10849
11525
  const { frontmatter, body } = splitFrontmatter(content);
10850
- config2._body = body;
10851
- if (frontmatter !== null) config2._frontmatter = frontmatter;
10852
- return config2;
10853
- }
10854
- var DEFAULT_MDC_FRONTMATTER = `description: "AI coding rules"
10855
- globs: ["**/*"]
10856
- alwaysApply: true`;
10857
- function generateTargetContent(config2, targetPlatform) {
10858
- const rawContent = config2._body ?? config2._raw ?? "";
10859
- const sourceFrontmatter = config2._frontmatter;
10860
- const mdcFrontmatter = sourceFrontmatter && /^(alwaysApply|globs)\s*:/m.test(sourceFrontmatter) ? sourceFrontmatter : DEFAULT_MDC_FRONTMATTER;
10861
- if (isCommandTarget(targetPlatform)) {
10862
- return config2._raw || "";
10863
- }
10864
- switch (targetPlatform) {
10865
- case "cursor":
10866
- case "roocode":
10867
- case "kilocode":
10868
- case "firebase":
10869
- return `---
10870
- ${mdcFrontmatter}
10871
- ---
10872
-
10873
- ${rawContent}`;
10874
- case "kiro":
10875
- return `---
10876
- inclusion: always
10877
- ---
10878
-
10879
- ${rawContent}`;
10880
- case "augment":
10881
- return `---
10882
- type: always_apply
10883
- ---
10884
-
10885
- ${rawContent}`;
10886
- case "cursor_legacy":
10887
- return rawContent.replace(/^#\s+/gm, "").replace(/^##\s+/gm, "\n").replace(/^###\s+/gm, "").trim();
10888
- case "windsurf":
10889
- case "cline":
10890
- case "goose":
10891
- case "trae":
10892
- return rawContent;
10893
- case "aider":
10894
- return `# Aider configuration
10895
- # Converted from AI IDE configuration
10896
-
10897
- lint-cmd: []
10898
- auto-test: false
10899
- read: []
10900
- # AI rules converted below as conventions
10901
- conventions:
10902
- ${rawContent.split("\n").filter((line) => line.trim() && !line.startsWith("#")).map((line) => ` - "${line.trim().replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`).join("\n")}
10903
- `;
10904
- case "tabnine":
10905
- return `# Tabnine configuration
10906
- # Converted from AI IDE configuration
10907
- instructions: |
10908
- ${rawContent.split("\n").map((line) => ` ${line}`).join("\n")}
10909
- `;
10910
- case "void":
10911
- case "continue":
10912
- case "cody":
10913
- case "codegpt":
10914
- case "supermaven":
10915
- case "opencode":
10916
- case "firebender":
10917
- return JSON.stringify({ instructions: rawContent }, null, 2) + "\n";
10918
- case "copilot":
10919
- return `# GitHub Copilot Instructions
10920
-
10921
- ${rawContent}
10922
- `;
10923
- case "agents":
10924
- case "universal":
10925
- case "claude":
10926
- case "codex":
10927
- case "antigravity":
10928
- case "gemini-cli":
10929
- case "zed":
10930
- case "amazonq":
10931
- case "junie":
10932
- case "warp":
10933
- case "openhands":
10934
- case "crush":
10935
- default:
10936
- return rawContent;
10937
- }
11526
+ const target = PLATFORM_FRONTMATTER[toPlatform];
11527
+ if (fromPlatform === toPlatform && (frontmatter || !target)) return content;
11528
+ if (frontmatter && target && PLATFORM_FRONTMATTER[fromPlatform] === target) return content;
11529
+ const lines = frontmatterLines(toPlatform, description);
11530
+ return lines.length ? `${lines.join("\n")}
11531
+ ${body}` : body;
10938
11532
  }
10939
11533
 
10940
- // src/commands/convert.ts
11534
+ // src/utils/convert-format.ts
11535
+ var isCommandTarget = (target) => target.includes("-command") || target.includes("-prompt") || target.includes("-workflow");
11536
+ var RULES_DESCRIPTION = "AI rules for this project";
11537
+ function isStructuredPlatform(platform2) {
11538
+ const format = getPlatform(platform2)?.format;
11539
+ return format === "json" || format === "yaml";
11540
+ }
11541
+ function platformForPath(filePath) {
11542
+ const normalized = filePath.replace(/\\/g, "/").toLowerCase();
11543
+ const rules = PLATFORMS.filter((p) => !p.isCommand);
11544
+ const inFolder2 = rules.find((p) => p.rulesDirectory && `/${normalized}`.includes(`/${p.rulesDirectory.toLowerCase()}`));
11545
+ if (inFolder2) return inFolder2.id;
11546
+ const file = rules.find((p) => {
11547
+ const own = p.file.toLowerCase();
11548
+ return normalized === own || normalized.endsWith(`/${own}`);
11549
+ });
11550
+ if (file) return file.id;
11551
+ return normalized.endsWith(".mdc") ? "cursor" : void 0;
11552
+ }
11553
+ function convertContent(content, sourcePlatform, targetPlatform) {
11554
+ if (isCommandTarget(targetPlatform)) return content;
11555
+ return adaptRulesContent(content, sourcePlatform, targetPlatform, RULES_DESCRIPTION);
11556
+ }
10941
11557
  var SOURCE_FILES = {
10942
11558
  "agents.md": "agents",
10943
11559
  "claude.md": "claude",
@@ -10949,54 +11565,38 @@ var SOURCE_FILES = {
10949
11565
  "copilot-instructions.md": "copilot",
10950
11566
  ".windsurfrules": "windsurf",
10951
11567
  ".clinerules": "cline",
10952
- ".aider.conf.yml": "aider"
11568
+ // Real case: auto-detect looks the name up as is, and Linux disks are case-sensitive
11569
+ "CONVENTIONS.md": "aider"
10953
11570
  };
11571
+ var sourceFilePlatform = (name) => Object.entries(SOURCE_FILES).find(([file]) => file.toLowerCase() === name.toLowerCase())?.[1];
11572
+ async function detectSourceFile(cwd) {
11573
+ for (const [pattern, platform2] of Object.entries(SOURCE_FILES)) {
11574
+ try {
11575
+ const fullPath = join9(cwd, pattern);
11576
+ await access5(fullPath);
11577
+ return { path: fullPath, platform: platform2 };
11578
+ } catch {
11579
+ }
11580
+ }
11581
+ const uppercaseVariants = ["AGENTS.md", "CLAUDE.md"];
11582
+ for (const variant of uppercaseVariants) {
11583
+ try {
11584
+ const fullPath = join9(cwd, variant);
11585
+ await access5(fullPath);
11586
+ const platform2 = variant.toLowerCase().replace(".md", "");
11587
+ return { path: fullPath, platform: platform2 };
11588
+ } catch {
11589
+ }
11590
+ }
11591
+ return null;
11592
+ }
11593
+
11594
+ // src/commands/convert.ts
10954
11595
  var TARGET_FILES = {
10955
- // Popular platforms
10956
- universal: "AGENTS.md",
10957
- agents: "AGENTS.md",
10958
- cursor: ".cursor/rules/project.mdc",
11596
+ ...PLATFORM_FILES2,
10959
11597
  cursor_legacy: ".cursorrules",
10960
- claude: "CLAUDE.md",
10961
- copilot: ".github/copilot-instructions.md",
10962
- windsurf: ".windsurfrules",
10963
- // AI-powered IDEs
10964
- antigravity: "GEMINI.md",
10965
- zed: ".zed/instructions.md",
10966
- void: ".void/config.json",
10967
- trae: ".trae/rules/project_rules.md",
10968
- firebase: ".idx/rules/project.mdc",
10969
- // Editor extensions
10970
- cline: ".clinerules",
10971
- roocode: ".roo/rules/project.mdc",
10972
- continue: ".continue/config.json",
10973
- cody: ".cody/config.json",
10974
- tabnine: ".tabnine.yaml",
10975
- supermaven: ".supermaven/config.json",
10976
- codegpt: ".codegpt/config.json",
10977
- amazonq: ".amazonq/rules/project.md",
10978
- augment: ".augment/rules/project.md",
10979
- kilocode: ".kilocode/rules/project.mdc",
10980
- junie: ".junie/guidelines.md",
10981
- kiro: ".kiro/steering/project.md",
10982
- // CLI tools
10983
- aider: ".aider.conf.yml",
10984
- goose: ".goosehints",
10985
- warp: "WARP.md",
10986
- "gemini-cli": "GEMINI.md",
10987
- opencode: "opencode.json",
10988
11598
  codex: "codex.md",
10989
- // Other emerging tools
10990
- openhands: ".openhands/microagents/repo.md",
10991
- crush: "CRUSH.md",
10992
- firebender: "firebender.json",
10993
- // Command targets
10994
- "cursor-command": ".cursor/commands/command.md",
10995
- "claude-command": ".claude/commands/command.md",
10996
- "windsurf-workflow": ".windsurf/workflows/workflow.md",
10997
- "copilot-prompt": ".copilot/prompts/prompt.md",
10998
- "continue-prompt": ".continue/prompts/prompt.md",
10999
- "opencode-command": ".opencode/commands/command.md"
11599
+ ...Object.fromEntries(COMMANDS.map((c) => [c.id, `${c.directory}/command${c.extension}`]))
11000
11600
  };
11001
11601
  var PLATFORM_NAMES = {
11002
11602
  universal: "AGENTS.md (Universal)",
@@ -11040,34 +11640,9 @@ var PLATFORM_NAMES = {
11040
11640
  "opencode-command": "OpenCode Command"
11041
11641
  };
11042
11642
  var COMMAND_DIRS = {
11043
- ".cursor/commands": "cursor-command",
11044
- ".claude/commands": "claude-command",
11045
- ".windsurf/workflows": "windsurf-workflow",
11046
- ".copilot/prompts": "copilot-prompt",
11047
- ".continue/prompts": "continue-prompt",
11048
- ".opencode/commands": "opencode-command"
11643
+ ...Object.fromEntries(COMMANDS.map((c) => [c.directory, c.id])),
11644
+ ".copilot/prompts": "copilot-prompt"
11049
11645
  };
11050
- async function detectSourceFile(cwd) {
11051
- for (const [pattern, platform2] of Object.entries(SOURCE_FILES)) {
11052
- try {
11053
- const fullPath = join8(cwd, pattern);
11054
- await access5(fullPath);
11055
- return { path: fullPath, platform: platform2 };
11056
- } catch {
11057
- }
11058
- }
11059
- const uppercaseVariants = ["AGENTS.md", "CLAUDE.md"];
11060
- for (const variant of uppercaseVariants) {
11061
- try {
11062
- const fullPath = join8(cwd, variant);
11063
- await access5(fullPath);
11064
- const platform2 = variant.toLowerCase().replace(".md", "");
11065
- return { path: fullPath, platform: platform2 };
11066
- } catch {
11067
- }
11068
- }
11069
- return null;
11070
- }
11071
11646
  function detectCommandPlatform(sourcePath) {
11072
11647
  const normalized = sourcePath.replace(/\\/g, "/").toLowerCase();
11073
11648
  for (const [dir, platform2] of Object.entries(COMMAND_DIRS)) {
@@ -11080,8 +11655,8 @@ function detectCommandPlatform(sourcePath) {
11080
11655
  async function convertCommand(first, second, options) {
11081
11656
  const [source, target] = second === void 0 ? [void 0, first] : [first, second];
11082
11657
  console.log();
11083
- console.log(chalk15.cyan.bold(" \u{1F504} LynxPrompt Convert"));
11084
- console.log(chalk15.gray(" Convert AI IDE configuration between formats"));
11658
+ console.log(chalk16.cyan.bold(" \u{1F504} LynxPrompt Convert"));
11659
+ console.log(chalk16.gray(" Convert AI IDE configuration between formats"));
11085
11660
  console.log();
11086
11661
  const cwd = process.cwd();
11087
11662
  let sourcePath;
@@ -11092,27 +11667,26 @@ async function convertCommand(first, second, options) {
11092
11667
  if (cmdPlatform) {
11093
11668
  sourcePlatform = cmdPlatform;
11094
11669
  } else {
11095
- const sourceBasename = basename(source).toLowerCase();
11096
- sourcePlatform = SOURCE_FILES[sourceBasename] || "unknown";
11670
+ sourcePlatform = platformForPath(source) || sourceFilePlatform(basename2(source)) || "unknown";
11097
11671
  }
11098
11672
  } else {
11099
11673
  const detected = await detectSourceFile(cwd);
11100
11674
  if (!detected) {
11101
- console.log(chalk15.red(" \u2717 No AI configuration file found in current directory"));
11675
+ console.log(chalk16.red(" \u2717 No AI configuration file found in current directory"));
11102
11676
  console.log();
11103
- console.log(chalk15.gray(" Supported source files:"));
11677
+ console.log(chalk16.gray(" Supported source files:"));
11104
11678
  for (const file of Object.keys(SOURCE_FILES)) {
11105
- console.log(chalk15.gray(` \u2022 ${file}`));
11679
+ console.log(chalk16.gray(` \u2022 ${file}`));
11106
11680
  }
11107
11681
  console.log();
11108
- console.log(chalk15.gray(" Command directories (for slash commands):"));
11682
+ console.log(chalk16.gray(" Command directories (for slash commands):"));
11109
11683
  for (const dir of Object.keys(COMMAND_DIRS)) {
11110
- console.log(chalk15.gray(` \u2022 ${dir}/*.md`));
11684
+ console.log(chalk16.gray(` \u2022 ${dir}/*.md`));
11111
11685
  }
11112
11686
  console.log();
11113
- console.log(chalk15.gray(" Usage: lynxp convert <source> <target>"));
11114
- console.log(chalk15.gray(" Example: lynxp convert AGENTS.md cursor"));
11115
- console.log(chalk15.gray(" Example: lynxp convert .cursor/commands/deploy.md claude-command"));
11687
+ console.log(chalk16.gray(" Usage: lynxp convert <source> <target>"));
11688
+ console.log(chalk16.gray(" Example: lynxp convert AGENTS.md cursor"));
11689
+ console.log(chalk16.gray(" Example: lynxp convert .cursor/commands/deploy.md claude-command"));
11116
11690
  process.exit(1);
11117
11691
  }
11118
11692
  sourcePath = detected.path;
@@ -11121,66 +11695,72 @@ async function convertCommand(first, second, options) {
11121
11695
  const targetLower = target.toLowerCase();
11122
11696
  const normalizedTarget = TARGET_FILES[targetLower] ? targetLower : targetLower.replace(/-/g, "_");
11123
11697
  if (!TARGET_FILES[normalizedTarget]) {
11124
- console.log(chalk15.red(` \u2717 Unknown target format: ${target}`));
11698
+ console.log(chalk16.red(` \u2717 Unknown target format: ${target}`));
11125
11699
  console.log();
11126
- console.log(chalk15.gray(" Supported target formats:"));
11700
+ console.log(chalk16.gray(" Supported target formats:"));
11127
11701
  for (const [key, name] of Object.entries(PLATFORM_NAMES)) {
11128
- console.log(chalk15.gray(` \u2022 ${key.padEnd(15)} \u2192 ${name}`));
11702
+ if (isStructuredPlatform(key)) continue;
11703
+ console.log(chalk16.gray(` \u2022 ${key.padEnd(15)} \u2192 ${name}`));
11129
11704
  }
11130
11705
  process.exit(1);
11131
11706
  }
11707
+ const structured = [sourcePlatform, normalizedTarget].find(isStructuredPlatform);
11708
+ if (structured) {
11709
+ console.log(chalk16.red(` \u2717 ${PLATFORM_NAMES[structured] || structured} uses a JSON/YAML config, not a rules file, so it cannot be converted`));
11710
+ console.log(chalk16.gray(` Run 'lynxp wizard' and pick ${PLATFORM_NAMES[structured] || structured} to generate one`));
11711
+ process.exit(1);
11712
+ }
11132
11713
  let sourceContent;
11133
11714
  try {
11134
- sourceContent = await readFile8(sourcePath, "utf-8");
11715
+ sourceContent = await readFile9(sourcePath, "utf-8");
11135
11716
  } catch {
11136
- console.log(chalk15.red(` \u2717 Could not read source file: ${sourcePath}`));
11717
+ console.log(chalk16.red(` \u2717 Could not read source file: ${sourcePath}`));
11137
11718
  process.exit(1);
11138
11719
  }
11139
- console.log(chalk15.white(` Source: ${basename(sourcePath)} (${PLATFORM_NAMES[sourcePlatform] || sourcePlatform})`));
11140
- console.log(chalk15.white(` Target: ${PLATFORM_NAMES[normalizedTarget]}`));
11720
+ console.log(chalk16.white(` Source: ${basename2(sourcePath)} (${PLATFORM_NAMES[sourcePlatform] || sourcePlatform})`));
11721
+ console.log(chalk16.white(` Target: ${PLATFORM_NAMES[normalizedTarget]}`));
11141
11722
  console.log();
11142
11723
  const spinner = ora12("Converting configuration...").start();
11143
- const config2 = parseMarkdownConfig(sourceContent);
11144
- const targetContent = generateTargetContent(config2, normalizedTarget);
11724
+ const targetContent = convertContent(sourceContent, sourcePlatform, normalizedTarget);
11145
11725
  spinner.stop();
11146
11726
  let outputFilename = options.output || TARGET_FILES[normalizedTarget];
11147
- if (isCommandTarget(normalizedTarget) && !options.output) {
11148
- const originalFilename = basename(sourcePath);
11149
- const targetDir = TARGET_FILES[normalizedTarget].split("/").slice(0, -1).join("/");
11150
- outputFilename = `${targetDir}/${originalFilename}`;
11727
+ const targetCommand = COMMANDS.find((c) => c.id === normalizedTarget);
11728
+ if (targetCommand && !options.output) {
11729
+ const commandName = basename2(sourcePath).replace(/(\.prompt)?\.md$/, "");
11730
+ outputFilename = `${targetCommand.directory}/${commandName}${targetCommand.extension}`;
11151
11731
  }
11152
11732
  const outputPath = resolve2(cwd, outputFilename);
11153
11733
  try {
11154
- await access5(outputPath);
11734
+ await access6(outputPath);
11155
11735
  if (!options.force) {
11156
- console.log(chalk15.yellow(` \u26A0\uFE0F File already exists: ${outputFilename}`));
11157
- console.log(chalk15.gray(" Use --force to overwrite"));
11736
+ console.log(chalk16.yellow(` \u26A0\uFE0F File already exists: ${outputFilename}`));
11737
+ console.log(chalk16.gray(" Use --force to overwrite"));
11158
11738
  process.exit(1);
11159
11739
  }
11160
11740
  } catch {
11161
11741
  }
11162
11742
  try {
11163
- const { mkdir: mkdir4 } = await import("fs/promises");
11164
- const outputDir = dirname4(outputPath);
11743
+ const { mkdir: mkdir5 } = await import("fs/promises");
11744
+ const outputDir = dirname5(outputPath);
11165
11745
  if (outputDir !== cwd) {
11166
- await mkdir4(outputDir, { recursive: true });
11746
+ await mkdir5(outputDir, { recursive: true });
11167
11747
  }
11168
- await writeFile4(outputPath, targetContent, "utf-8");
11169
- console.log(chalk15.green(` \u2713 Converted to ${outputFilename}`));
11748
+ await writeFile5(outputPath, targetContent, "utf-8");
11749
+ console.log(chalk16.green(` \u2713 Converted to ${outputFilename}`));
11170
11750
  console.log();
11171
- console.log(chalk15.gray(` Lines: ${targetContent.split("\n").length}`));
11172
- console.log(chalk15.gray(` Size: ${targetContent.length} bytes`));
11751
+ console.log(chalk16.gray(` Lines: ${targetContent.split("\n").length}`));
11752
+ console.log(chalk16.gray(` Size: ${targetContent.length} bytes`));
11173
11753
  console.log();
11174
11754
  } catch (error) {
11175
- console.log(chalk15.red(` \u2717 Could not write output: ${error instanceof Error ? error.message : "unknown error"}`));
11755
+ console.log(chalk16.red(` \u2717 Could not write output: ${error instanceof Error ? error.message : "unknown error"}`));
11176
11756
  process.exit(1);
11177
11757
  }
11178
11758
  }
11179
11759
 
11180
11760
  // src/commands/merge.ts
11181
- import chalk16 from "chalk";
11182
- import { readFile as readFile9, writeFile as writeFile5, access as access6 } from "fs/promises";
11183
- import { basename as basename2, resolve as resolve3 } from "path";
11761
+ import chalk17 from "chalk";
11762
+ import { readFile as readFile10, writeFile as writeFile6, access as access7 } from "fs/promises";
11763
+ import { basename as basename3, resolve as resolve3 } from "path";
11184
11764
  import ora13 from "ora";
11185
11765
  import prompts5 from "prompts";
11186
11766
 
@@ -11276,43 +11856,43 @@ ${s.content.trim()}`).join("\n\n");
11276
11856
  // src/commands/merge.ts
11277
11857
  async function mergeCommand(files, options) {
11278
11858
  console.log();
11279
- console.log(chalk16.cyan.bold(" \u{1F500} LynxPrompt Merge"));
11280
- console.log(chalk16.gray(" Merge multiple AI IDE configuration files"));
11859
+ console.log(chalk17.cyan.bold(" \u{1F500} LynxPrompt Merge"));
11860
+ console.log(chalk17.gray(" Merge multiple AI IDE configuration files"));
11281
11861
  console.log();
11282
11862
  if (files.length < 2) {
11283
- console.log(chalk16.red(" \u2717 Please provide at least 2 files to merge"));
11863
+ console.log(chalk17.red(" \u2717 Please provide at least 2 files to merge"));
11284
11864
  console.log();
11285
- console.log(chalk16.gray(" Usage: lynxp merge <file1> <file2> [...files]"));
11286
- console.log(chalk16.gray(" Example: lynxp merge AGENTS.md team-rules.md --output merged.md"));
11865
+ console.log(chalk17.gray(" Usage: lynxp merge <file1> <file2> [...files]"));
11866
+ console.log(chalk17.gray(" Example: lynxp merge AGENTS.md team-rules.md --output merged.md"));
11287
11867
  console.log();
11288
- console.log(chalk16.gray(" Options:"));
11289
- console.log(chalk16.gray(" --output <file> Output filename (default: merged.md)"));
11290
- console.log(chalk16.gray(" --strategy <type> Merge strategy: concat, sections, smart (default: smart)"));
11291
- console.log(chalk16.gray(" --force Overwrite existing output file"));
11292
- console.log(chalk16.gray(" --interactive Review and select sections to include"));
11868
+ console.log(chalk17.gray(" Options:"));
11869
+ console.log(chalk17.gray(" --output <file> Output filename (default: merged.md)"));
11870
+ console.log(chalk17.gray(" --strategy <type> Merge strategy: concat, sections, smart (default: smart)"));
11871
+ console.log(chalk17.gray(" --force Overwrite existing output file"));
11872
+ console.log(chalk17.gray(" --interactive Review and select sections to include"));
11293
11873
  process.exit(1);
11294
11874
  }
11295
11875
  const cwd = process.cwd();
11296
11876
  const strategy = options.strategy || "smart";
11297
- console.log(chalk16.white(` Files to merge: ${files.length}`));
11298
- console.log(chalk16.white(` Strategy: ${strategy}`));
11877
+ console.log(chalk17.white(` Files to merge: ${files.length}`));
11878
+ console.log(chalk17.white(` Strategy: ${strategy}`));
11299
11879
  console.log();
11300
11880
  const allSections = [];
11301
11881
  for (const file of files) {
11302
11882
  const filePath = resolve3(cwd, file);
11303
11883
  try {
11304
- await access6(filePath);
11884
+ await access7(filePath);
11305
11885
  } catch {
11306
- console.log(chalk16.red(` \u2717 File not found: ${file}`));
11886
+ console.log(chalk17.red(` \u2717 File not found: ${file}`));
11307
11887
  process.exit(1);
11308
11888
  }
11309
11889
  try {
11310
- const content = await readFile9(filePath, "utf-8");
11311
- const sections = parseMarkdownSections(content, basename2(file));
11890
+ const content = await readFile10(filePath, "utf-8");
11891
+ const sections = parseMarkdownSections(content, basename3(file));
11312
11892
  allSections.push(sections);
11313
- console.log(chalk16.gray(` \u2713 Read ${file} (${sections.length} sections)`));
11893
+ console.log(chalk17.gray(` \u2713 Read ${file} (${sections.length} sections)`));
11314
11894
  } catch (error) {
11315
- console.log(chalk16.red(` \u2717 Could not read ${file}: ${error instanceof Error ? error.message : "unknown"}`));
11895
+ console.log(chalk17.red(` \u2717 Could not read ${file}: ${error instanceof Error ? error.message : "unknown"}`));
11316
11896
  process.exit(1);
11317
11897
  }
11318
11898
  }
@@ -11335,7 +11915,7 @@ async function mergeCommand(files, options) {
11335
11915
  hint: "- Space to toggle, Enter to confirm"
11336
11916
  });
11337
11917
  if (!response.sections || response.sections.length === 0) {
11338
- console.log(chalk16.yellow(" No sections selected, aborting."));
11918
+ console.log(chalk17.yellow(" No sections selected, aborting."));
11339
11919
  process.exit(0);
11340
11920
  }
11341
11921
  const selectedSections = response.sections.map((i) => flatSections[i]);
@@ -11358,34 +11938,34 @@ ${mergedContent}
11358
11938
  const outputFilename = options.output || "merged.md";
11359
11939
  const outputPath = resolve3(cwd, outputFilename);
11360
11940
  try {
11361
- await access6(outputPath);
11941
+ await access7(outputPath);
11362
11942
  if (!options.force) {
11363
- console.log(chalk16.yellow(` \u26A0\uFE0F File already exists: ${outputFilename}`));
11364
- console.log(chalk16.gray(" Use --force to overwrite"));
11943
+ console.log(chalk17.yellow(` \u26A0\uFE0F File already exists: ${outputFilename}`));
11944
+ console.log(chalk17.gray(" Use --force to overwrite"));
11365
11945
  process.exit(1);
11366
11946
  }
11367
11947
  } catch {
11368
11948
  }
11369
11949
  try {
11370
- await writeFile5(outputPath, finalContent, "utf-8");
11371
- console.log(chalk16.green(` \u2713 Merged to ${outputFilename}`));
11950
+ await writeFile6(outputPath, finalContent, "utf-8");
11951
+ console.log(chalk17.green(` \u2713 Merged to ${outputFilename}`));
11372
11952
  console.log();
11373
- console.log(chalk16.gray(` Sources: ${files.length} files`));
11374
- console.log(chalk16.gray(` Lines: ${finalContent.split("\n").length}`));
11375
- console.log(chalk16.gray(` Size: ${finalContent.length} bytes`));
11953
+ console.log(chalk17.gray(` Sources: ${files.length} files`));
11954
+ console.log(chalk17.gray(` Lines: ${finalContent.split("\n").length}`));
11955
+ console.log(chalk17.gray(` Size: ${finalContent.length} bytes`));
11376
11956
  console.log();
11377
11957
  } catch (error) {
11378
- console.log(chalk16.red(` \u2717 Could not write output: ${error instanceof Error ? error.message : "unknown error"}`));
11958
+ console.log(chalk17.red(` \u2717 Could not write output: ${error instanceof Error ? error.message : "unknown error"}`));
11379
11959
  process.exit(1);
11380
11960
  }
11381
11961
  }
11382
11962
 
11383
11963
  // src/commands/import.ts
11384
- import chalk17 from "chalk";
11964
+ import chalk18 from "chalk";
11385
11965
  import prompts6 from "prompts";
11386
11966
  import ora14 from "ora";
11387
- import { readFile as readFile10, access as access7, readdir as readdir3 } from "fs/promises";
11388
- import { join as join9, relative as relative3, dirname as dirname5, basename as basename3, resolve as resolve4 } from "path";
11967
+ import { readFile as readFile11, access as access8, readdir as readdir3 } from "fs/promises";
11968
+ import { join as join10, relative as relative3, dirname as dirname6, basename as basename4, resolve as resolve4 } from "path";
11389
11969
  var CONFIG_FILE_PATTERNS = [
11390
11970
  "AGENTS.md",
11391
11971
  "CLAUDE.md",
@@ -11402,7 +11982,7 @@ async function scanDirectory(rootPath, options = {}) {
11402
11982
  try {
11403
11983
  const entries = await readdir3(dir, { withFileTypes: true });
11404
11984
  for (const entry of entries) {
11405
- const fullPath = join9(dir, entry.name);
11985
+ const fullPath = join10(dir, entry.name);
11406
11986
  if (entry.isDirectory()) {
11407
11987
  const skipDirs = [
11408
11988
  "node_modules",
@@ -11428,13 +12008,13 @@ async function scanDirectory(rootPath, options = {}) {
11428
12008
  } else if (entry.isFile()) {
11429
12009
  if (patterns.includes(entry.name)) {
11430
12010
  try {
11431
- const content = await readFile10(fullPath, "utf-8");
12011
+ const content = await readFile11(fullPath, "utf-8");
11432
12012
  const relativePath = relative3(rootPath, fullPath);
11433
12013
  files.push({
11434
12014
  path: fullPath,
11435
12015
  relativePath,
11436
12016
  depth,
11437
- name: extractProjectName(content, dirname5(fullPath)),
12017
+ name: extractProjectName(content, dirname6(fullPath)),
11438
12018
  content,
11439
12019
  sections: parseMarkdownSections2(content)
11440
12020
  });
@@ -11465,7 +12045,7 @@ function extractProjectName(content, dirPath) {
11465
12045
  }
11466
12046
  }
11467
12047
  }
11468
- return basename3(dirPath) || "Unnamed";
12048
+ return basename4(dirPath) || "Unnamed";
11469
12049
  }
11470
12050
  function parseMarkdownSections2(content) {
11471
12051
  const sections = [];
@@ -11501,13 +12081,13 @@ function buildHierarchy(files, _rootPath) {
11501
12081
  const potentialRoots = sorted.filter((f) => f.depth <= 1);
11502
12082
  if (potentialRoots.length === 0 && files.length > 0) {
11503
12083
  return files.map((f) => ({
11504
- rootPath: dirname5(f.path),
12084
+ rootPath: dirname6(f.path),
11505
12085
  root: f,
11506
12086
  children: []
11507
12087
  }));
11508
12088
  }
11509
12089
  for (const root of potentialRoots) {
11510
- const rootDir = dirname5(root.path);
12090
+ const rootDir = dirname6(root.path);
11511
12091
  const children = files.filter((f) => {
11512
12092
  if (f === root) return false;
11513
12093
  return f.path.startsWith(rootDir + "/") && f.depth > root.depth;
@@ -11525,7 +12105,7 @@ function buildHierarchy(files, _rootPath) {
11525
12105
  const orphans = files.filter((f) => !assignedPaths.has(f.path));
11526
12106
  for (const orphan of orphans) {
11527
12107
  hierarchies.push({
11528
- rootPath: dirname5(orphan.path),
12108
+ rootPath: dirname6(orphan.path),
11529
12109
  root: orphan,
11530
12110
  children: []
11531
12111
  });
@@ -11538,33 +12118,33 @@ function displayResults(result, options) {
11538
12118
  return;
11539
12119
  }
11540
12120
  console.log();
11541
- console.log(chalk17.cyan.bold(" \u{1F4E5} Import Results"));
12121
+ console.log(chalk18.cyan.bold(" \u{1F4E5} Import Results"));
11542
12122
  console.log();
11543
12123
  if (result.totalFound === 0) {
11544
- console.log(chalk17.yellow(" No AGENTS.md or compatible files found."));
11545
- console.log(chalk17.gray(" Try running from your project root, or use --pattern to specify custom filenames."));
12124
+ console.log(chalk18.yellow(" No AGENTS.md or compatible files found."));
12125
+ console.log(chalk18.gray(" Try running from your project root, or use --pattern to specify custom filenames."));
11546
12126
  return;
11547
12127
  }
11548
- console.log(chalk17.green(` Found ${result.totalFound} configuration file(s)`));
12128
+ console.log(chalk18.green(` Found ${result.totalFound} configuration file(s)`));
11549
12129
  console.log();
11550
12130
  for (const hierarchy of result.hierarchy) {
11551
12131
  const isMonorepo = hierarchy.children.length > 0;
11552
12132
  if (hierarchy.root) {
11553
12133
  const icon = isMonorepo ? "\u{1F3E2}" : "\u{1F4C4}";
11554
- const typeLabel = isMonorepo ? chalk17.magenta("[Monorepo Root]") : chalk17.blue("[Standalone]");
11555
- console.log(` ${icon} ${chalk17.white.bold(hierarchy.root.name)} ${typeLabel}`);
11556
- console.log(chalk17.gray(` ${hierarchy.root.relativePath}`));
12134
+ const typeLabel = isMonorepo ? chalk18.magenta("[Monorepo Root]") : chalk18.blue("[Standalone]");
12135
+ console.log(` ${icon} ${chalk18.white.bold(hierarchy.root.name)} ${typeLabel}`);
12136
+ console.log(chalk18.gray(` ${hierarchy.root.relativePath}`));
11557
12137
  if (options.verbose && hierarchy.root.sections.length > 0) {
11558
12138
  const sectionNames = hierarchy.root.sections.slice(0, 5).map((s) => s.title);
11559
- console.log(chalk17.gray(` Sections: ${sectionNames.join(", ")}${hierarchy.root.sections.length > 5 ? "..." : ""}`));
12139
+ console.log(chalk18.gray(` Sections: ${sectionNames.join(", ")}${hierarchy.root.sections.length > 5 ? "..." : ""}`));
11560
12140
  }
11561
12141
  }
11562
12142
  for (const child of hierarchy.children) {
11563
- console.log(` \u2514\u2500 \u{1F4C4} ${chalk17.white(child.name)}`);
11564
- console.log(chalk17.gray(` ${child.relativePath}`));
12143
+ console.log(` \u2514\u2500 \u{1F4C4} ${chalk18.white(child.name)}`);
12144
+ console.log(chalk18.gray(` ${child.relativePath}`));
11565
12145
  if (options.verbose && child.sections.length > 0) {
11566
12146
  const sectionNames = child.sections.slice(0, 3).map((s) => s.title);
11567
- console.log(chalk17.gray(` Sections: ${sectionNames.join(", ")}${child.sections.length > 3 ? "..." : ""}`));
12147
+ console.log(chalk18.gray(` Sections: ${sectionNames.join(", ")}${child.sections.length > 3 ? "..." : ""}`));
11568
12148
  }
11569
12149
  }
11570
12150
  console.log();
@@ -11572,16 +12152,16 @@ function displayResults(result, options) {
11572
12152
  const monorepoCount = result.hierarchy.filter((h) => h.children.length > 0).length;
11573
12153
  const standaloneCount = result.hierarchy.filter((h) => h.children.length === 0).length;
11574
12154
  if (monorepoCount > 0) {
11575
- console.log(chalk17.cyan(` \u{1F4CA} ${monorepoCount} monorepo(s) detected with hierarchical configs`));
12155
+ console.log(chalk18.cyan(` \u{1F4CA} ${monorepoCount} monorepo(s) detected with hierarchical configs`));
11576
12156
  }
11577
12157
  if (standaloneCount > 0) {
11578
- console.log(chalk17.cyan(` \u{1F4CA} ${standaloneCount} standalone config(s)`));
12158
+ console.log(chalk18.cyan(` \u{1F4CA} ${standaloneCount} standalone config(s)`));
11579
12159
  }
11580
12160
  if (result.errors.length > 0) {
11581
12161
  console.log();
11582
- console.log(chalk17.yellow(" \u26A0\uFE0F Warnings:"));
12162
+ console.log(chalk18.yellow(" \u26A0\uFE0F Warnings:"));
11583
12163
  for (const error of result.errors) {
11584
- console.log(chalk17.gray(` ${error}`));
12164
+ console.log(chalk18.gray(` ${error}`));
11585
12165
  }
11586
12166
  }
11587
12167
  }
@@ -11589,15 +12169,15 @@ async function importCommand(path3 = ".", options) {
11589
12169
  const json = Boolean(options.json);
11590
12170
  if (!json) {
11591
12171
  console.log();
11592
- console.log(chalk17.cyan.bold(" \u{1F4E5} LynxPrompt Import"));
11593
- console.log(chalk17.gray(" Scan and import AGENTS.md files and AI commands from your repository"));
12172
+ console.log(chalk18.cyan.bold(" \u{1F4E5} LynxPrompt Import"));
12173
+ console.log(chalk18.gray(" Scan and import AGENTS.md files and AI commands from your repository"));
11594
12174
  console.log();
11595
12175
  }
11596
12176
  const rootPath = resolve4(process.cwd(), path3);
11597
12177
  try {
11598
- await access7(rootPath);
12178
+ await access8(rootPath);
11599
12179
  } catch {
11600
- console.error(chalk17.red(` \u2717 Path not found: ${rootPath}`));
12180
+ console.error(chalk18.red(` \u2717 Path not found: ${rootPath}`));
11601
12181
  process.exit(1);
11602
12182
  }
11603
12183
  const spinner = ora14({ text: "Scanning for configuration files and commands...", isSilent: json }).start();
@@ -11612,10 +12192,10 @@ async function importCommand(path3 = ".", options) {
11612
12192
  }
11613
12193
  spinner.warn("No configuration files or commands found");
11614
12194
  console.log();
11615
- console.log(chalk17.gray(" Looking for:"));
11616
- console.log(chalk17.gray(" \u2022 Rules: AGENTS.md, CLAUDE.md, .cursorrules, .windsurfrules"));
11617
- console.log(chalk17.gray(" \u2022 Commands: .cursor/commands/*.md, .claude/commands/*.md"));
11618
- console.log(chalk17.gray(" Try specifying a different path or use --pattern for custom filenames"));
12195
+ console.log(chalk18.gray(" Looking for:"));
12196
+ console.log(chalk18.gray(" \u2022 Rules: AGENTS.md, CLAUDE.md, .cursorrules, .windsurfrules"));
12197
+ console.log(chalk18.gray(" \u2022 Commands: .cursor/commands/*.md, .claude/commands/*.md"));
12198
+ console.log(chalk18.gray(" Try specifying a different path or use --pattern for custom filenames"));
11619
12199
  return;
11620
12200
  }
11621
12201
  if (json) spinner.stop();
@@ -11632,31 +12212,31 @@ async function importCommand(path3 = ".", options) {
11632
12212
  if (json) return;
11633
12213
  if (commands.length > 0) {
11634
12214
  console.log();
11635
- console.log(chalk17.cyan.bold(" \u26A1 AI Agent Commands"));
12215
+ console.log(chalk18.cyan.bold(" \u26A1 AI Agent Commands"));
11636
12216
  console.log();
11637
12217
  const cursorCommands = commands.filter((c) => c.type === "cursor-command");
11638
12218
  const claudeCommands = commands.filter((c) => c.type === "claude-command");
11639
12219
  if (cursorCommands.length > 0) {
11640
- console.log(chalk17.blue(` Cursor Commands (${cursorCommands.length}):`));
12220
+ console.log(chalk18.blue(` Cursor Commands (${cursorCommands.length}):`));
11641
12221
  for (const cmd of cursorCommands) {
11642
- console.log(chalk17.gray(` \u26A1 ${cmd.name}`));
11643
- console.log(chalk17.gray(` ${relative3(rootPath, cmd.path)}`));
12222
+ console.log(chalk18.gray(` \u26A1 ${cmd.name}`));
12223
+ console.log(chalk18.gray(` ${relative3(rootPath, cmd.path)}`));
11644
12224
  }
11645
12225
  console.log();
11646
12226
  }
11647
12227
  if (claudeCommands.length > 0) {
11648
- console.log(chalk17.yellow(` Claude Commands (${claudeCommands.length}):`));
12228
+ console.log(chalk18.yellow(` Claude Commands (${claudeCommands.length}):`));
11649
12229
  for (const cmd of claudeCommands) {
11650
- console.log(chalk17.gray(` \u{1F9E0} ${cmd.name}`));
11651
- console.log(chalk17.gray(` ${relative3(rootPath, cmd.path)}`));
12230
+ console.log(chalk18.gray(` \u{1F9E0} ${cmd.name}`));
12231
+ console.log(chalk18.gray(` ${relative3(rootPath, cmd.path)}`));
11652
12232
  }
11653
12233
  console.log();
11654
12234
  }
11655
- console.log(chalk17.cyan(" \u{1F4A1} Push commands with: lynxp push .cursor/commands/my-command.md"));
12235
+ console.log(chalk18.cyan(" \u{1F4A1} Push commands with: lynxp push .cursor/commands/my-command.md"));
11656
12236
  }
11657
12237
  if (options.dryRun) {
11658
- console.log(chalk17.yellow(" \u{1F4CB} Dry run complete - no changes made"));
11659
- console.log(chalk17.gray(" Remove --dry-run to proceed with import"));
12238
+ console.log(chalk18.yellow(" \u{1F4CB} Dry run complete - no changes made"));
12239
+ console.log(chalk18.gray(" Remove --dry-run to proceed with import"));
11660
12240
  return;
11661
12241
  }
11662
12242
  if (!options.json) {
@@ -11675,15 +12255,15 @@ async function importCommand(path3 = ".", options) {
11675
12255
  });
11676
12256
  switch (action) {
11677
12257
  case "convert":
11678
- console.log(chalk17.cyan("\n \u{1F4A1} Use 'lynxp convert <file> agents' to convert individual files"));
11679
- console.log(chalk17.cyan(" or 'lynxp merge <files...>' to combine multiple configs\n"));
12258
+ console.log(chalk18.cyan("\n \u{1F4A1} Use 'lynxp convert <file> agents' to convert individual files"));
12259
+ console.log(chalk18.cyan(" or 'lynxp merge <files...>' to combine multiple configs\n"));
11680
12260
  break;
11681
12261
  case "save":
11682
12262
  await saveHierarchyInfo(rootPath, result);
11683
12263
  break;
11684
12264
  case "push":
11685
- console.log(chalk17.cyan("\n \u{1F4A1} Use 'lynxp push <file>' to push files individually"));
11686
- console.log(chalk17.cyan(" Requires login: 'lynxp login'\n"));
12265
+ console.log(chalk18.cyan("\n \u{1F4A1} Use 'lynxp push <file>' to push files individually"));
12266
+ console.log(chalk18.cyan(" Requires login: 'lynxp login'\n"));
11687
12267
  break;
11688
12268
  default:
11689
12269
  break;
@@ -11691,15 +12271,15 @@ async function importCommand(path3 = ".", options) {
11691
12271
  }
11692
12272
  } catch (error) {
11693
12273
  spinner.fail("Import failed");
11694
- console.error(chalk17.red(` Error: ${error instanceof Error ? error.message : "Unknown error"}`));
12274
+ console.error(chalk18.red(` Error: ${error instanceof Error ? error.message : "Unknown error"}`));
11695
12275
  process.exit(1);
11696
12276
  }
11697
12277
  }
11698
12278
  async function saveHierarchyInfo(rootPath, result) {
11699
- const { writeFile: writeFile6, mkdir: mkdir4 } = await import("fs/promises");
11700
- const configDir = join9(rootPath, ".lynxprompt");
12279
+ const { writeFile: writeFile7, mkdir: mkdir5 } = await import("fs/promises");
12280
+ const configDir = join10(rootPath, ".lynxprompt");
11701
12281
  try {
11702
- await mkdir4(configDir, { recursive: true });
12282
+ await mkdir5(configDir, { recursive: true });
11703
12283
  const hierarchyInfo = {
11704
12284
  scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
11705
12285
  rootPath,
@@ -11715,25 +12295,25 @@ async function saveHierarchyInfo(rootPath, result) {
11715
12295
  }))
11716
12296
  }))
11717
12297
  };
11718
- await writeFile6(
11719
- join9(configDir, "hierarchy.json"),
12298
+ await writeFile7(
12299
+ join10(configDir, "hierarchy.json"),
11720
12300
  JSON.stringify(hierarchyInfo, null, 2),
11721
12301
  "utf-8"
11722
12302
  );
11723
- console.log(chalk17.green("\n \u2713 Hierarchy saved to .lynxprompt/hierarchy.json"));
11724
- console.log(chalk17.gray(" The wizard will use this to understand your monorepo structure"));
12303
+ console.log(chalk18.green("\n \u2713 Hierarchy saved to .lynxprompt/hierarchy.json"));
12304
+ console.log(chalk18.gray(" The wizard will use this to understand your monorepo structure"));
11725
12305
  } catch (error) {
11726
- console.error(chalk17.red(` \u2717 Failed to save hierarchy: ${error instanceof Error ? error.message : "Unknown"}`));
12306
+ console.error(chalk18.red(` \u2717 Failed to save hierarchy: ${error instanceof Error ? error.message : "Unknown"}`));
11727
12307
  }
11728
12308
  }
11729
12309
 
11730
12310
  // src/commands/hierarchies.ts
11731
- import chalk18 from "chalk";
12311
+ import chalk19 from "chalk";
11732
12312
  import ora15 from "ora";
11733
12313
  async function hierarchiesCommand(options) {
11734
12314
  if (!isAuthenticated()) {
11735
- console.log(chalk18.yellow("You need to be logged in to view hierarchies."));
11736
- console.log(chalk18.gray("Run 'lynxp login' to authenticate."));
12315
+ console.log(chalk19.yellow("You need to be logged in to view hierarchies."));
12316
+ console.log(chalk19.gray("Run 'lynxp login' to authenticate."));
11737
12317
  process.exit(1);
11738
12318
  }
11739
12319
  const spinner = ora15("Fetching hierarchies...").start();
@@ -11749,32 +12329,32 @@ async function hierarchiesCommand(options) {
11749
12329
  const { hierarchies, total } = response;
11750
12330
  if (hierarchies.length === 0) {
11751
12331
  console.log();
11752
- console.log(chalk18.yellow("No hierarchies found."));
12332
+ console.log(chalk19.yellow("No hierarchies found."));
11753
12333
  console.log();
11754
- console.log(chalk18.gray("Hierarchies are created automatically when you push AGENTS.md files from a repository."));
11755
- console.log(chalk18.gray("Run 'lynxp push AGENTS.md' in a git repository to create a hierarchy."));
12334
+ console.log(chalk19.gray("Hierarchies are created automatically when you push AGENTS.md files from a repository."));
12335
+ console.log(chalk19.gray("Run 'lynxp push AGENTS.md' in a git repository to create a hierarchy."));
11756
12336
  console.log();
11757
12337
  return;
11758
12338
  }
11759
12339
  console.log();
11760
- console.log(chalk18.cyan(`\u{1F4C1} Your Hierarchies (${total} total)`));
12340
+ console.log(chalk19.cyan(`\u{1F4C1} Your Hierarchies (${total} total)`));
11761
12341
  console.log();
11762
12342
  for (const hierarchy of hierarchies) {
11763
- console.log(chalk18.bold(` ${hierarchy.name}`));
11764
- console.log(chalk18.gray(` ID: ${hierarchy.id}`));
12343
+ console.log(chalk19.bold(` ${hierarchy.name}`));
12344
+ console.log(chalk19.gray(` ID: ${hierarchy.id}`));
11765
12345
  if (hierarchy.description) {
11766
- console.log(chalk18.gray(` ${hierarchy.description}`));
12346
+ console.log(chalk19.gray(` ${hierarchy.description}`));
11767
12347
  }
11768
- console.log(chalk18.gray(` Blueprints: ${hierarchy.blueprint_count || 0}`));
11769
- console.log(chalk18.gray(` Repository: ${hierarchy.repository_root.slice(0, 16)}...`));
12348
+ console.log(chalk19.gray(` Blueprints: ${hierarchy.blueprint_count || 0}`));
12349
+ console.log(chalk19.gray(` Repository: ${hierarchy.repository_root.slice(0, 16)}...`));
11770
12350
  console.log();
11771
12351
  }
11772
- console.log(chalk18.gray("\u2500".repeat(50)));
12352
+ console.log(chalk19.gray("\u2500".repeat(50)));
11773
12353
  console.log();
11774
- console.log(chalk18.gray("Tips:"));
11775
- console.log(chalk18.gray(` \u2022 Pull entire hierarchy: lynxp pull <hierarchy_id>`));
11776
- console.log(chalk18.gray(` \u2022 Preview a hierarchy: lynxp pull <hierarchy_id> --preview`));
11777
- console.log(chalk18.gray(` \u2022 Push local changes: lynxp push --all`));
12354
+ console.log(chalk19.gray("Tips:"));
12355
+ console.log(chalk19.gray(` \u2022 Pull entire hierarchy: lynxp pull <hierarchy_id>`));
12356
+ console.log(chalk19.gray(` \u2022 Preview a hierarchy: lynxp pull <hierarchy_id> --preview`));
12357
+ console.log(chalk19.gray(` \u2022 Push local changes: lynxp push --all`));
11778
12358
  console.log();
11779
12359
  } catch (error) {
11780
12360
  spinner.fail("Failed to fetch hierarchies");
@@ -11783,34 +12363,46 @@ async function hierarchiesCommand(options) {
11783
12363
  }
11784
12364
  function handleError2(error) {
11785
12365
  if (error instanceof ApiRequestError) {
11786
- console.error(chalk18.red(`Error: ${error.message}`));
12366
+ console.error(chalk19.red(`Error: ${error.message}`));
11787
12367
  if (error.statusCode === 401) {
11788
- console.error(chalk18.gray("Your session may have expired. Run 'lynxp login' to re-authenticate."));
12368
+ console.error(chalk19.gray("Your session may have expired. Run 'lynxp login' to re-authenticate."));
11789
12369
  } else if (error.statusCode === 403) {
11790
- console.error(chalk18.gray("You don't have permission to access hierarchies."));
12370
+ console.error(chalk19.gray("You don't have permission to access hierarchies."));
11791
12371
  }
11792
12372
  } else {
11793
- console.error(chalk18.red("An unexpected error occurred."));
12373
+ console.error(chalk19.red("An unexpected error occurred."));
11794
12374
  }
11795
12375
  process.exit(1);
11796
12376
  }
11797
12377
 
11798
12378
  // src/commands/config.ts
11799
- import chalk19 from "chalk";
12379
+ import chalk20 from "chalk";
12380
+ function printLoginState() {
12381
+ if (isAuthenticated()) {
12382
+ const email = getUser()?.email;
12383
+ console.log(chalk20.gray(` Logged in${email ? ` as ${email}` : ""} on this instance.`));
12384
+ } else {
12385
+ console.log(chalk20.gray(" Run 'lynxp login' to authenticate with this instance."));
12386
+ }
12387
+ }
11800
12388
  async function configCommand(action, valueArg) {
11801
12389
  if (!action || action === "show") {
11802
12390
  console.log();
11803
- console.log(chalk19.cyan("\u2699\uFE0F LynxPrompt CLI Configuration"));
12391
+ console.log(chalk20.cyan("\u2699\uFE0F LynxPrompt CLI Configuration"));
11804
12392
  console.log();
11805
12393
  console.log(
11806
- chalk19.white(" API URL: ") + chalk19.green(getApiUrl())
12394
+ chalk20.white(" API URL: ") + chalk20.green(getApiUrl())
12395
+ );
12396
+ const loggedIn = isAuthenticated();
12397
+ console.log(
12398
+ chalk20.white(" Login: ") + (loggedIn ? chalk20.green(getUser()?.email ?? "token set") : chalk20.gray("not logged in"))
11807
12399
  );
11808
12400
  console.log(
11809
- chalk19.white(" Config: ") + chalk19.gray(getConfigPath())
12401
+ chalk20.white(" Config: ") + chalk20.gray(getConfigPath())
11810
12402
  );
11811
12403
  if (process.env.LYNXPROMPT_API_URL) {
11812
12404
  console.log(
11813
- chalk19.gray(
12405
+ chalk20.gray(
11814
12406
  " (API URL overridden by LYNXPROMPT_API_URL env var)"
11815
12407
  )
11816
12408
  );
@@ -11821,44 +12413,43 @@ async function configCommand(action, valueArg) {
11821
12413
  if (action === "set-url") {
11822
12414
  if (!valueArg) {
11823
12415
  console.error(
11824
- chalk19.red("Usage: lynxp config set-url <url>")
12416
+ chalk20.red("Usage: lynxp config set-url <url>")
11825
12417
  );
11826
12418
  process.exit(1);
11827
12419
  }
11828
12420
  try {
11829
12421
  new URL(valueArg);
11830
12422
  } catch {
11831
- console.error(chalk19.red(`Invalid URL: ${valueArg}`));
12423
+ console.error(chalk20.red(`Invalid URL: ${valueArg}`));
11832
12424
  process.exit(1);
11833
12425
  }
11834
12426
  const clean = valueArg.replace(/\/+$/, "");
11835
12427
  setApiUrl(clean);
11836
12428
  console.log(
11837
- chalk19.green("\u2713") + ` API URL set to ${chalk19.cyan(clean)}`
11838
- );
11839
- console.log(
11840
- chalk19.gray(" Run 'lynxp login' to authenticate with this instance.")
12429
+ chalk20.green("\u2713") + ` API URL set to ${chalk20.cyan(clean)}`
11841
12430
  );
12431
+ printLoginState();
11842
12432
  return;
11843
12433
  }
11844
12434
  if (action === "reset-url") {
11845
- setApiUrl("https://api.lynxprompt.com");
12435
+ const url = resetApiUrl();
11846
12436
  console.log(
11847
- chalk19.green("\u2713") + " API URL reset to " + chalk19.cyan("https://api.lynxprompt.com")
12437
+ chalk20.green("\u2713") + " API URL reset to " + chalk20.cyan(url)
11848
12438
  );
12439
+ printLoginState();
11849
12440
  return;
11850
12441
  }
11851
12442
  if (action === "path") {
11852
12443
  console.log(getConfigPath());
11853
12444
  return;
11854
12445
  }
11855
- console.error(chalk19.red(`Unknown config action: ${action}`));
11856
- console.error(chalk19.gray("Available: show, set-url <url>, reset-url, path"));
12446
+ console.error(chalk20.red(`Unknown config action: ${action}`));
12447
+ console.error(chalk20.gray("Available: show, set-url <url>, reset-url, path"));
11857
12448
  process.exit(1);
11858
12449
  }
11859
12450
 
11860
12451
  // src/index.ts
11861
- var CLI_VERSION2 = "2.1.38";
12452
+ var CLI_VERSION2 = "2.1.40";
11862
12453
  var program = new Command();
11863
12454
  program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version(CLI_VERSION2);
11864
12455
  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)").option("-o, --output <dir>", "Output directory (default: current directory)").option("--repo-url <url>", "Analyze remote repository URL (GitHub/GitLab supported)").option("--blueprint", "Generate with [[VARIABLE|default]] placeholders for templates").option("--license <type>", "License type (mit, apache-2.0, gpl-3.0, etc.)").option("--ci-cd <platform>", "CI/CD platform (github_actions, gitlab_ci, jenkins, etc.)").option("--project-type <type>", "Project type (work, leisure, opensource, learning)").option("--detect-only", "Only detect project info, don't generate files").option("--load-draft <name>", "Load a saved wizard draft").option("--save-draft <name>", "Save wizard state as a draft (auto-saves at end)").option("--vars <values>", "Fill variables: VAR1=value1,VAR2=value2").action(wizardCommand);
@@ -11868,7 +12459,7 @@ program.command("convert <source> [target]").description("Convert AI config betw
11868
12459
  program.command("merge <files...>").description("Merge multiple AI configuration files into one").option("-o, --output <file>", "Output filename (default: merged.md)").option("-s, --strategy <type>", "Merge strategy: concat, sections, smart (default: smart)").option("-f, --force", "Overwrite existing output file").option("-i, --interactive", "Review and select sections to include").action(mergeCommand);
11869
12460
  program.command("import [path]").description("Scan and import AGENTS.md files from a repository").option("--dry-run", "Preview what would be imported without changes").option("--no-recursive", "Don't scan subdirectories").option("--depth <n>", "Max directory depth to scan", "10").option("--pattern <file>", "Custom config filename to look for").option("-v, --verbose", "Show detailed section information").option("-j, --json", "Output as JSON").action(importCommand);
11870
12461
  program.command("status").description("Show current AI configuration and tracked blueprints").action(statusCommand);
11871
- program.command("pull <id>").description("Download a blueprint (bp_xxx) or entire hierarchy (ha_xxx)").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);
12462
+ program.command("pull <id>").description("Download a blueprint (bp_xxx) or entire hierarchy (ha_xxx)").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").option("--vars <values>", "Fill [[VARIABLES]]: VAR1=value1,VAR2=value2 (others get their defaults)").action(pullCommand);
11872
12463
  program.command("search <query>").description("Search public blueprints in the marketplace").option("-l, --limit <number>", "Number of results", "20").action(searchCommand);
11873
12464
  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);
11874
12465
  program.command("push [file]").description("Push local file to LynxPrompt cloud as a blueprint").option("-a, --all", "Scan recursively and push ALL AI config files (40+ types)").option("-n, --name <name>", "Blueprint name").option("-d, --description <desc>", "Blueprint description").option("-v, --visibility <vis>", "Visibility: PRIVATE, TEAM, or PUBLIC (default: PRIVATE)").option("-t, --tags <tags>", "Tags (comma-separated)").option("-y, --yes", "Skip prompts").option("-f, --force", "Force push (overwrite remote changes)").option("--allow-sensitive", "Publish public content even if it looks like it holds secrets").action(pushCommand);
@@ -11876,6 +12467,7 @@ program.command("hierarchies").description("List your blueprint hierarchies (mon
11876
12467
  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);
11877
12468
  program.command("unlink [file]").description("Disconnect a local file from its cloud blueprint").action(unlinkCommand);
11878
12469
  program.command("diff [file-or-id]").description("Compare tracked files with their cloud blueprints").option("--local", "Compare .lynxprompt/rules/ with exported files").action(diffCommand);
12470
+ program.command("sync [file]").description("Pull cloud changes and push local ones for tracked files; conflicts go to <file>.remote").option("--dry-run", "Show what would change without writing or pushing").action(syncCommand);
11879
12471
  program.command("login").description("Authenticate with LynxPrompt (opens browser)").action(loginCommand);
11880
12472
  program.command("logout").description("Log out and remove stored credentials").action(logoutCommand);
11881
12473
  program.command("whoami").description("Show current authenticated user").action(whoamiCommand);
@@ -11883,51 +12475,53 @@ program.command("config [action] [value]").description("View or change CLI setti
11883
12475
  program.addHelpText(
11884
12476
  "beforeAll",
11885
12477
  `
11886
- ${chalk20.cyan("\u{1F431} LynxPrompt CLI")} ${chalk20.gray("(also available as: lynxp)")}
11887
- ${chalk20.gray("Generate AI IDE configuration files from your terminal")}
12478
+ ${chalk21.cyan("\u{1F431} LynxPrompt CLI")} ${chalk21.gray("(also available as: lynxp)")}
12479
+ ${chalk21.gray("Generate AI IDE configuration files from your terminal")}
11888
12480
  `
11889
12481
  );
11890
12482
  program.addHelpText(
11891
12483
  "after",
11892
12484
  `
11893
- ${chalk20.cyan("Quick Start:")}
11894
- ${chalk20.white("$ lynxp wizard")} ${chalk20.gray("Generate config interactively")}
11895
- ${chalk20.white("$ lynxp wizard -y")} ${chalk20.gray("Generate AGENTS.md with defaults")}
11896
- ${chalk20.white("$ lynxp wizard -f cursor")} ${chalk20.gray("Generate .cursor/rules/")}
11897
- ${chalk20.white("$ lynxp wizard --blueprint")} ${chalk20.gray("Generate with [[VAR|default]] placeholders")}
11898
-
11899
- ${chalk20.cyan("Analysis & Tools:")}
11900
- ${chalk20.white("$ lynxp analyze")} ${chalk20.gray("Analyze project tech stack")}
11901
- ${chalk20.white("$ lynxp analyze -r <url>")} ${chalk20.gray("Analyze remote repository")}
11902
- ${chalk20.white("$ lynxp import")} ${chalk20.gray("Scan repo for AGENTS.md files")}
11903
- ${chalk20.white("$ lynxp import --dry-run")} ${chalk20.gray("Preview monorepo hierarchy")}
11904
- ${chalk20.white("$ lynxp convert AGENTS.md cursor")} ${chalk20.gray("Convert to Cursor format")}
11905
- ${chalk20.white("$ lynxp merge a.md b.md -o out.md")} ${chalk20.gray("Merge multiple configs")}
11906
-
11907
- ${chalk20.cyan("Marketplace:")}
11908
- ${chalk20.white("$ lynxp search nextjs")} ${chalk20.gray("Search blueprints")}
11909
- ${chalk20.white("$ lynxp pull bp_abc123")} ${chalk20.gray("Download and track a blueprint")}
11910
- ${chalk20.white("$ lynxp pull ha_xyz789")} ${chalk20.gray("Download entire hierarchy")}
11911
- ${chalk20.white("$ lynxp push")} ${chalk20.gray("Push local file to cloud")}
11912
- ${chalk20.white("$ lynxp push --all")} ${chalk20.gray("Push ALL config files recursively")}
11913
- ${chalk20.white("$ lynxp hierarchies")} ${chalk20.gray("List your hierarchies")}
11914
- ${chalk20.white("$ lynxp link --list")} ${chalk20.gray("Show tracked blueprints")}
11915
-
11916
- ${chalk20.cyan("Blueprint Tracking:")}
11917
- ${chalk20.white("$ lynxp link AGENTS.md bp_xyz")} ${chalk20.gray("Link existing file to blueprint")}
11918
- ${chalk20.white("$ lynxp unlink AGENTS.md")} ${chalk20.gray("Disconnect from cloud")}
11919
- ${chalk20.white("$ lynxp diff bp_abc123")} ${chalk20.gray("Show changes vs cloud version")}
11920
-
11921
- ${chalk20.cyan("CI/CD:")}
11922
- ${chalk20.white("$ lynxp check --ci")} ${chalk20.gray("Validate config (exit code)")}
11923
-
11924
- ${chalk20.cyan("Configuration:")}
11925
- ${chalk20.white("$ lynxp config")} ${chalk20.gray("Show current settings")}
11926
- ${chalk20.white("$ lynxp config set-url <url>")} ${chalk20.gray("Point CLI to a self-hosted instance")}
11927
- ${chalk20.white("$ lynxp config reset-url")} ${chalk20.gray("Reset to https://api.lynxprompt.com")}
11928
- ${chalk20.white("$ lynxp config path")} ${chalk20.gray("Show config file location")}
11929
-
11930
- ${chalk20.gray("Docs: https://lynxprompt.com/docs/cli")}
12485
+ ${chalk21.cyan("Quick Start:")}
12486
+ ${chalk21.white("$ lynxp wizard")} ${chalk21.gray("Generate config interactively")}
12487
+ ${chalk21.white("$ lynxp wizard -y")} ${chalk21.gray("Generate AGENTS.md with defaults")}
12488
+ ${chalk21.white("$ lynxp wizard -f cursor")} ${chalk21.gray("Generate .cursor/rules/")}
12489
+ ${chalk21.white("$ lynxp wizard --blueprint")} ${chalk21.gray("Generate with [[VAR|default]] placeholders")}
12490
+
12491
+ ${chalk21.cyan("Analysis & Tools:")}
12492
+ ${chalk21.white("$ lynxp analyze")} ${chalk21.gray("Analyze project tech stack")}
12493
+ ${chalk21.white("$ lynxp analyze -r <url>")} ${chalk21.gray("Analyze remote repository")}
12494
+ ${chalk21.white("$ lynxp import")} ${chalk21.gray("Scan repo for AGENTS.md files")}
12495
+ ${chalk21.white("$ lynxp import --dry-run")} ${chalk21.gray("Preview monorepo hierarchy")}
12496
+ ${chalk21.white("$ lynxp convert AGENTS.md cursor")} ${chalk21.gray("Convert to Cursor format")}
12497
+ ${chalk21.white("$ lynxp merge a.md b.md -o out.md")} ${chalk21.gray("Merge multiple configs")}
12498
+
12499
+ ${chalk21.cyan("Marketplace:")}
12500
+ ${chalk21.white("$ lynxp search nextjs")} ${chalk21.gray("Search blueprints")}
12501
+ ${chalk21.white("$ lynxp pull bp_abc123")} ${chalk21.gray("Download and track a blueprint")}
12502
+ ${chalk21.white("$ lynxp pull ha_xyz789")} ${chalk21.gray("Download entire hierarchy")}
12503
+ ${chalk21.white("$ lynxp push")} ${chalk21.gray("Push local file to cloud")}
12504
+ ${chalk21.white("$ lynxp push --all")} ${chalk21.gray("Push ALL config files recursively")}
12505
+ ${chalk21.white("$ lynxp hierarchies")} ${chalk21.gray("List your hierarchies")}
12506
+ ${chalk21.white("$ lynxp link --list")} ${chalk21.gray("Show tracked blueprints")}
12507
+
12508
+ ${chalk21.cyan("Blueprint Tracking:")}
12509
+ ${chalk21.white("$ lynxp link AGENTS.md bp_xyz")} ${chalk21.gray("Link existing file to blueprint")}
12510
+ ${chalk21.white("$ lynxp unlink AGENTS.md")} ${chalk21.gray("Disconnect from cloud")}
12511
+ ${chalk21.white("$ lynxp diff bp_abc123")} ${chalk21.gray("Show changes vs cloud version")}
12512
+ ${chalk21.white("$ lynxp sync")} ${chalk21.gray("Pull cloud changes, push local ones")}
12513
+ ${chalk21.white("$ lynxp pull bp_x --vars A=1")} ${chalk21.gray("Download with [[A]] filled in")}
12514
+
12515
+ ${chalk21.cyan("CI/CD:")}
12516
+ ${chalk21.white("$ lynxp check --ci")} ${chalk21.gray("Validate config (exit code)")}
12517
+
12518
+ ${chalk21.cyan("Configuration:")}
12519
+ ${chalk21.white("$ lynxp config")} ${chalk21.gray("Show current settings")}
12520
+ ${chalk21.white("$ lynxp config set-url <url>")} ${chalk21.gray("Point CLI to a self-hosted instance")}
12521
+ ${chalk21.white("$ lynxp config reset-url")} ${chalk21.gray("Reset to https://lynxprompt.com")}
12522
+ ${chalk21.white("$ lynxp config path")} ${chalk21.gray("Show config file location")}
12523
+
12524
+ ${chalk21.gray("Docs: https://lynxprompt.com/docs/cli")}
11931
12525
  `
11932
12526
  );
11933
12527
  program.parse();