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/README.md +28 -0
- package/dist/index.js +1361 -767
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
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
|
|
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
|
-
|
|
24
|
+
tokens: {
|
|
31
25
|
type: "object",
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
52
|
-
|
|
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
|
|
55
|
-
return
|
|
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
|
-
|
|
63
|
-
return void 0;
|
|
64
|
-
}
|
|
65
|
-
return config.get("token");
|
|
85
|
+
return currentLogin()?.token;
|
|
66
86
|
}
|
|
67
87
|
function setToken(token) {
|
|
68
|
-
|
|
69
|
-
|
|
88
|
+
const logins = getLogins();
|
|
89
|
+
logins[normalizeUrl(getApiUrl())] = { token };
|
|
90
|
+
setLogins(logins);
|
|
70
91
|
}
|
|
71
92
|
function getStoredCredentials() {
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
if (!
|
|
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:
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
96
|
-
return void 0;
|
|
97
|
-
}
|
|
98
|
-
return config.get("user");
|
|
124
|
+
return currentLogin()?.user;
|
|
99
125
|
}
|
|
100
126
|
function setUser(user) {
|
|
101
|
-
|
|
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(
|
|
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
|
|
796
|
-
|
|
797
|
-
|
|
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 =
|
|
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
|
|
1330
|
-
const
|
|
1331
|
-
const
|
|
1332
|
-
const
|
|
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
|
|
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 === "
|
|
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 =
|
|
2428
|
-
if (platform2 &&
|
|
2429
|
-
|
|
2430
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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.
|
|
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) => !
|
|
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(
|
|
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 ?
|
|
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: ".
|
|
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: "
|
|
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
|
|
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
|
|
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/
|
|
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
|
|
10337
|
-
import { existsSync as
|
|
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(
|
|
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
|
-
".
|
|
11035
|
+
".rules",
|
|
10369
11036
|
".clinerules"
|
|
10370
11037
|
];
|
|
10371
|
-
const foundFiles = configFiles.filter((f) =>
|
|
11038
|
+
const foundFiles = configFiles.filter((f) => existsSync6(join8(cwd, f)));
|
|
10372
11039
|
if (foundFiles.length === 0) {
|
|
10373
|
-
console.log(
|
|
11040
|
+
console.log(chalk14.yellow("No AI configuration files found in this directory."));
|
|
10374
11041
|
console.log();
|
|
10375
|
-
console.log(
|
|
10376
|
-
console.log(
|
|
10377
|
-
console.log(
|
|
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(
|
|
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 =
|
|
10399
|
-
if (!
|
|
10400
|
-
console.log(
|
|
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(
|
|
10406
|
-
console.log(
|
|
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(
|
|
11082
|
+
console.log(chalk14.gray("Cancelled."));
|
|
10416
11083
|
return;
|
|
10417
11084
|
}
|
|
10418
11085
|
}
|
|
10419
11086
|
if (!blueprintId) {
|
|
10420
11087
|
if (!isAuthenticated()) {
|
|
10421
|
-
console.log(
|
|
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(
|
|
11096
|
+
console.log(chalk14.gray("Run 'lynxp login' in another terminal, then come back here."));
|
|
10430
11097
|
return;
|
|
10431
11098
|
}
|
|
10432
|
-
console.log(
|
|
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(
|
|
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(
|
|
10456
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
11198
|
+
console.log(chalk14.red("\u2717 No blueprint ID provided."));
|
|
10532
11199
|
return;
|
|
10533
11200
|
}
|
|
10534
|
-
const spinner = ora10(`Fetching blueprint ${
|
|
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(
|
|
11208
|
+
console.log(chalk14.cyan(`\u{1F431} Blueprint: ${chalk14.bold(safeText(blueprint.name))}`));
|
|
10542
11209
|
if (blueprint.description) {
|
|
10543
|
-
console.log(
|
|
11210
|
+
console.log(chalk14.gray(` ${safeText(blueprint.description)}`));
|
|
10544
11211
|
}
|
|
10545
|
-
console.log(
|
|
11212
|
+
console.log(chalk14.gray(` Visibility: ${blueprint.visibility}`));
|
|
10546
11213
|
console.log();
|
|
10547
11214
|
if (isMarketplace) {
|
|
10548
|
-
console.log(
|
|
10549
|
-
console.log(
|
|
10550
|
-
console.log(
|
|
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 ${
|
|
11223
|
+
message: `Link ${chalk14.cyan(file)} to ${chalk14.cyan(blueprint.name)}?`,
|
|
10557
11224
|
initial: true
|
|
10558
11225
|
});
|
|
10559
11226
|
if (!confirm) {
|
|
10560
|
-
console.log(
|
|
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(
|
|
11232
|
+
console.log(chalk14.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
|
|
10566
11233
|
console.log();
|
|
10567
|
-
console.log(
|
|
10568
|
-
console.log(
|
|
10569
|
-
console.log(
|
|
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(
|
|
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(
|
|
10579
|
-
console.log(
|
|
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(
|
|
11248
|
+
console.log(chalk14.red("\u2717 You don't have access to this blueprint."));
|
|
10582
11249
|
} else {
|
|
10583
|
-
console.log(
|
|
11250
|
+
console.log(chalk14.red(`\u2717 Error: ${error.message}`));
|
|
10584
11251
|
}
|
|
10585
11252
|
} else {
|
|
10586
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
11289
|
+
console.log(chalk14.yellow(`File is not linked to any blueprint: ${file}`));
|
|
10623
11290
|
return;
|
|
10624
11291
|
}
|
|
10625
|
-
console.log(
|
|
10626
|
-
console.log(
|
|
10627
|
-
console.log(
|
|
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 ${
|
|
11299
|
+
message: `Unlink ${chalk14.cyan(file)} from ${chalk14.cyan(tracked.name)}?`,
|
|
10633
11300
|
initial: true
|
|
10634
11301
|
});
|
|
10635
11302
|
if (!confirm) {
|
|
10636
|
-
console.log(
|
|
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(
|
|
10643
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
11322
|
+
console.log(chalk14.gray("No blueprints are currently tracked."));
|
|
10656
11323
|
console.log();
|
|
10657
|
-
console.log(
|
|
10658
|
-
console.log(
|
|
10659
|
-
console.log(
|
|
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 ?
|
|
11330
|
+
const statusIcon = !fileExists2 ? chalk14.red("\u2717") : localModified ? chalk14.yellow("\u25CF") : chalk14.green("\u2713");
|
|
10664
11331
|
const sourceLabel = {
|
|
10665
|
-
marketplace:
|
|
10666
|
-
team:
|
|
10667
|
-
private:
|
|
10668
|
-
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} ${
|
|
11337
|
+
console.log(`${statusIcon} ${chalk14.cyan(blueprint.file)}`);
|
|
10671
11338
|
console.log(` ${sourceLabel} ${blueprint.name}`);
|
|
10672
|
-
console.log(` ${
|
|
11339
|
+
console.log(` ${chalk14.gray(`ID: ${blueprint.id}`)}`);
|
|
10673
11340
|
if (!fileExists2) {
|
|
10674
|
-
console.log(
|
|
11341
|
+
console.log(chalk14.red(` \u26A0 File not found`));
|
|
10675
11342
|
} else if (localModified) {
|
|
10676
|
-
console.log(
|
|
11343
|
+
console.log(chalk14.yellow(` \u26A0 Local changes detected`));
|
|
10677
11344
|
}
|
|
10678
11345
|
console.log();
|
|
10679
11346
|
}
|
|
10680
|
-
console.log(
|
|
10681
|
-
console.log(
|
|
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
|
|
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(
|
|
10692
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
11413
|
+
console.log(chalk15.yellow(" No project configuration detected."));
|
|
10747
11414
|
console.log();
|
|
10748
|
-
console.log(
|
|
10749
|
-
console.log(
|
|
10750
|
-
console.log(
|
|
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(
|
|
11421
|
+
console.log(chalk15.green.bold(" \u{1F4CA} Project Analysis"));
|
|
10755
11422
|
console.log();
|
|
10756
|
-
console.log(
|
|
10757
|
-
console.log(
|
|
10758
|
-
console.log(` ${
|
|
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(` ${
|
|
11427
|
+
console.log(` ${chalk15.dim("Description:")} ${chalk15.gray(detected.description)}`);
|
|
10761
11428
|
}
|
|
10762
|
-
console.log(` ${
|
|
11429
|
+
console.log(` ${chalk15.dim("Type:")} ${chalk15.white(detected.type || "application")}`);
|
|
10763
11430
|
if (detected.license) {
|
|
10764
|
-
console.log(` ${
|
|
11431
|
+
console.log(` ${chalk15.dim("License:")} ${chalk15.white(detected.license.toUpperCase())}`);
|
|
10765
11432
|
}
|
|
10766
11433
|
console.log();
|
|
10767
|
-
console.log(
|
|
10768
|
-
console.log(
|
|
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(` ${
|
|
11438
|
+
console.log(` ${chalk15.cyan("\u2022")} ${tech}`);
|
|
10772
11439
|
}
|
|
10773
11440
|
} else {
|
|
10774
|
-
console.log(
|
|
11441
|
+
console.log(chalk15.gray(" No tech stack detected"));
|
|
10775
11442
|
}
|
|
10776
11443
|
console.log();
|
|
10777
|
-
console.log(
|
|
10778
|
-
console.log(
|
|
10779
|
-
console.log(` ${
|
|
10780
|
-
console.log(` ${
|
|
10781
|
-
console.log(` ${
|
|
10782
|
-
console.log(` ${
|
|
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(
|
|
10786
|
-
console.log(
|
|
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(` ${
|
|
11457
|
+
console.log(` ${chalk15.dim(key.padEnd(10))} ${chalk15.yellow(cmdValue)}`);
|
|
10791
11458
|
}
|
|
10792
11459
|
}
|
|
10793
11460
|
console.log();
|
|
10794
11461
|
}
|
|
10795
|
-
console.log(
|
|
10796
|
-
console.log(
|
|
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(` ${
|
|
11482
|
+
console.log(` ${chalk15.yellow("\u2022")} ${rec}`);
|
|
10816
11483
|
}
|
|
10817
11484
|
} else {
|
|
10818
|
-
console.log(
|
|
11485
|
+
console.log(chalk15.green(" \u2713 Project looks well-configured!"));
|
|
10819
11486
|
}
|
|
10820
11487
|
console.log();
|
|
10821
|
-
console.log(
|
|
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
|
|
10827
|
-
import { readFile as
|
|
10828
|
-
import {
|
|
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
|
-
|
|
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(
|
|
10835
|
-
if (!match) return { frontmatter:
|
|
10836
|
-
return { frontmatter: match[
|
|
10837
|
-
}
|
|
10838
|
-
function
|
|
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
|
-
|
|
10851
|
-
if (frontmatter
|
|
10852
|
-
return
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11044
|
-
".
|
|
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(
|
|
11084
|
-
console.log(
|
|
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
|
-
|
|
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(
|
|
11675
|
+
console.log(chalk16.red(" \u2717 No AI configuration file found in current directory"));
|
|
11102
11676
|
console.log();
|
|
11103
|
-
console.log(
|
|
11677
|
+
console.log(chalk16.gray(" Supported source files:"));
|
|
11104
11678
|
for (const file of Object.keys(SOURCE_FILES)) {
|
|
11105
|
-
console.log(
|
|
11679
|
+
console.log(chalk16.gray(` \u2022 ${file}`));
|
|
11106
11680
|
}
|
|
11107
11681
|
console.log();
|
|
11108
|
-
console.log(
|
|
11682
|
+
console.log(chalk16.gray(" Command directories (for slash commands):"));
|
|
11109
11683
|
for (const dir of Object.keys(COMMAND_DIRS)) {
|
|
11110
|
-
console.log(
|
|
11684
|
+
console.log(chalk16.gray(` \u2022 ${dir}/*.md`));
|
|
11111
11685
|
}
|
|
11112
11686
|
console.log();
|
|
11113
|
-
console.log(
|
|
11114
|
-
console.log(
|
|
11115
|
-
console.log(
|
|
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(
|
|
11698
|
+
console.log(chalk16.red(` \u2717 Unknown target format: ${target}`));
|
|
11125
11699
|
console.log();
|
|
11126
|
-
console.log(
|
|
11700
|
+
console.log(chalk16.gray(" Supported target formats:"));
|
|
11127
11701
|
for (const [key, name] of Object.entries(PLATFORM_NAMES)) {
|
|
11128
|
-
|
|
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
|
|
11715
|
+
sourceContent = await readFile9(sourcePath, "utf-8");
|
|
11135
11716
|
} catch {
|
|
11136
|
-
console.log(
|
|
11717
|
+
console.log(chalk16.red(` \u2717 Could not read source file: ${sourcePath}`));
|
|
11137
11718
|
process.exit(1);
|
|
11138
11719
|
}
|
|
11139
|
-
console.log(
|
|
11140
|
-
console.log(
|
|
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
|
|
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
|
-
|
|
11148
|
-
|
|
11149
|
-
const
|
|
11150
|
-
outputFilename = `${
|
|
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
|
|
11734
|
+
await access6(outputPath);
|
|
11155
11735
|
if (!options.force) {
|
|
11156
|
-
console.log(
|
|
11157
|
-
console.log(
|
|
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:
|
|
11164
|
-
const outputDir =
|
|
11743
|
+
const { mkdir: mkdir5 } = await import("fs/promises");
|
|
11744
|
+
const outputDir = dirname5(outputPath);
|
|
11165
11745
|
if (outputDir !== cwd) {
|
|
11166
|
-
await
|
|
11746
|
+
await mkdir5(outputDir, { recursive: true });
|
|
11167
11747
|
}
|
|
11168
|
-
await
|
|
11169
|
-
console.log(
|
|
11748
|
+
await writeFile5(outputPath, targetContent, "utf-8");
|
|
11749
|
+
console.log(chalk16.green(` \u2713 Converted to ${outputFilename}`));
|
|
11170
11750
|
console.log();
|
|
11171
|
-
console.log(
|
|
11172
|
-
console.log(
|
|
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(
|
|
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
|
|
11182
|
-
import { readFile as
|
|
11183
|
-
import { basename as
|
|
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(
|
|
11280
|
-
console.log(
|
|
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(
|
|
11863
|
+
console.log(chalk17.red(" \u2717 Please provide at least 2 files to merge"));
|
|
11284
11864
|
console.log();
|
|
11285
|
-
console.log(
|
|
11286
|
-
console.log(
|
|
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(
|
|
11289
|
-
console.log(
|
|
11290
|
-
console.log(
|
|
11291
|
-
console.log(
|
|
11292
|
-
console.log(
|
|
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(
|
|
11298
|
-
console.log(
|
|
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
|
|
11884
|
+
await access7(filePath);
|
|
11305
11885
|
} catch {
|
|
11306
|
-
console.log(
|
|
11886
|
+
console.log(chalk17.red(` \u2717 File not found: ${file}`));
|
|
11307
11887
|
process.exit(1);
|
|
11308
11888
|
}
|
|
11309
11889
|
try {
|
|
11310
|
-
const content = await
|
|
11311
|
-
const sections = parseMarkdownSections(content,
|
|
11890
|
+
const content = await readFile10(filePath, "utf-8");
|
|
11891
|
+
const sections = parseMarkdownSections(content, basename3(file));
|
|
11312
11892
|
allSections.push(sections);
|
|
11313
|
-
console.log(
|
|
11893
|
+
console.log(chalk17.gray(` \u2713 Read ${file} (${sections.length} sections)`));
|
|
11314
11894
|
} catch (error) {
|
|
11315
|
-
console.log(
|
|
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(
|
|
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
|
|
11941
|
+
await access7(outputPath);
|
|
11362
11942
|
if (!options.force) {
|
|
11363
|
-
console.log(
|
|
11364
|
-
console.log(
|
|
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
|
|
11371
|
-
console.log(
|
|
11950
|
+
await writeFile6(outputPath, finalContent, "utf-8");
|
|
11951
|
+
console.log(chalk17.green(` \u2713 Merged to ${outputFilename}`));
|
|
11372
11952
|
console.log();
|
|
11373
|
-
console.log(
|
|
11374
|
-
console.log(
|
|
11375
|
-
console.log(
|
|
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(
|
|
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
|
|
11964
|
+
import chalk18 from "chalk";
|
|
11385
11965
|
import prompts6 from "prompts";
|
|
11386
11966
|
import ora14 from "ora";
|
|
11387
|
-
import { readFile as
|
|
11388
|
-
import { join as
|
|
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 =
|
|
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
|
|
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,
|
|
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
|
|
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:
|
|
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 =
|
|
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:
|
|
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(
|
|
12121
|
+
console.log(chalk18.cyan.bold(" \u{1F4E5} Import Results"));
|
|
11542
12122
|
console.log();
|
|
11543
12123
|
if (result.totalFound === 0) {
|
|
11544
|
-
console.log(
|
|
11545
|
-
console.log(
|
|
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(
|
|
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 ?
|
|
11555
|
-
console.log(` ${icon} ${
|
|
11556
|
-
console.log(
|
|
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(
|
|
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} ${
|
|
11564
|
-
console.log(
|
|
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(
|
|
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(
|
|
12155
|
+
console.log(chalk18.cyan(` \u{1F4CA} ${monorepoCount} monorepo(s) detected with hierarchical configs`));
|
|
11576
12156
|
}
|
|
11577
12157
|
if (standaloneCount > 0) {
|
|
11578
|
-
console.log(
|
|
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(
|
|
12162
|
+
console.log(chalk18.yellow(" \u26A0\uFE0F Warnings:"));
|
|
11583
12163
|
for (const error of result.errors) {
|
|
11584
|
-
console.log(
|
|
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(
|
|
11593
|
-
console.log(
|
|
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
|
|
12178
|
+
await access8(rootPath);
|
|
11599
12179
|
} catch {
|
|
11600
|
-
console.error(
|
|
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(
|
|
11616
|
-
console.log(
|
|
11617
|
-
console.log(
|
|
11618
|
-
console.log(
|
|
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(
|
|
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(
|
|
12220
|
+
console.log(chalk18.blue(` Cursor Commands (${cursorCommands.length}):`));
|
|
11641
12221
|
for (const cmd of cursorCommands) {
|
|
11642
|
-
console.log(
|
|
11643
|
-
console.log(
|
|
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(
|
|
12228
|
+
console.log(chalk18.yellow(` Claude Commands (${claudeCommands.length}):`));
|
|
11649
12229
|
for (const cmd of claudeCommands) {
|
|
11650
|
-
console.log(
|
|
11651
|
-
console.log(
|
|
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(
|
|
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(
|
|
11659
|
-
console.log(
|
|
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(
|
|
11679
|
-
console.log(
|
|
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(
|
|
11686
|
-
console.log(
|
|
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(
|
|
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:
|
|
11700
|
-
const configDir =
|
|
12279
|
+
const { writeFile: writeFile7, mkdir: mkdir5 } = await import("fs/promises");
|
|
12280
|
+
const configDir = join10(rootPath, ".lynxprompt");
|
|
11701
12281
|
try {
|
|
11702
|
-
await
|
|
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
|
|
11719
|
-
|
|
12298
|
+
await writeFile7(
|
|
12299
|
+
join10(configDir, "hierarchy.json"),
|
|
11720
12300
|
JSON.stringify(hierarchyInfo, null, 2),
|
|
11721
12301
|
"utf-8"
|
|
11722
12302
|
);
|
|
11723
|
-
console.log(
|
|
11724
|
-
console.log(
|
|
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(
|
|
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
|
|
12311
|
+
import chalk19 from "chalk";
|
|
11732
12312
|
import ora15 from "ora";
|
|
11733
12313
|
async function hierarchiesCommand(options) {
|
|
11734
12314
|
if (!isAuthenticated()) {
|
|
11735
|
-
console.log(
|
|
11736
|
-
console.log(
|
|
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(
|
|
12332
|
+
console.log(chalk19.yellow("No hierarchies found."));
|
|
11753
12333
|
console.log();
|
|
11754
|
-
console.log(
|
|
11755
|
-
console.log(
|
|
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(
|
|
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(
|
|
11764
|
-
console.log(
|
|
12343
|
+
console.log(chalk19.bold(` ${hierarchy.name}`));
|
|
12344
|
+
console.log(chalk19.gray(` ID: ${hierarchy.id}`));
|
|
11765
12345
|
if (hierarchy.description) {
|
|
11766
|
-
console.log(
|
|
12346
|
+
console.log(chalk19.gray(` ${hierarchy.description}`));
|
|
11767
12347
|
}
|
|
11768
|
-
console.log(
|
|
11769
|
-
console.log(
|
|
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(
|
|
12352
|
+
console.log(chalk19.gray("\u2500".repeat(50)));
|
|
11773
12353
|
console.log();
|
|
11774
|
-
console.log(
|
|
11775
|
-
console.log(
|
|
11776
|
-
console.log(
|
|
11777
|
-
console.log(
|
|
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(
|
|
12366
|
+
console.error(chalk19.red(`Error: ${error.message}`));
|
|
11787
12367
|
if (error.statusCode === 401) {
|
|
11788
|
-
console.error(
|
|
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(
|
|
12370
|
+
console.error(chalk19.gray("You don't have permission to access hierarchies."));
|
|
11791
12371
|
}
|
|
11792
12372
|
} else {
|
|
11793
|
-
console.error(
|
|
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
|
|
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(
|
|
12391
|
+
console.log(chalk20.cyan("\u2699\uFE0F LynxPrompt CLI Configuration"));
|
|
11804
12392
|
console.log();
|
|
11805
12393
|
console.log(
|
|
11806
|
-
|
|
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
|
-
|
|
12401
|
+
chalk20.white(" Config: ") + chalk20.gray(getConfigPath())
|
|
11810
12402
|
);
|
|
11811
12403
|
if (process.env.LYNXPROMPT_API_URL) {
|
|
11812
12404
|
console.log(
|
|
11813
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
12435
|
+
const url = resetApiUrl();
|
|
11846
12436
|
console.log(
|
|
11847
|
-
|
|
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(
|
|
11856
|
-
console.error(
|
|
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.
|
|
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
|
-
${
|
|
11887
|
-
${
|
|
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
|
-
${
|
|
11894
|
-
${
|
|
11895
|
-
${
|
|
11896
|
-
${
|
|
11897
|
-
${
|
|
11898
|
-
|
|
11899
|
-
${
|
|
11900
|
-
${
|
|
11901
|
-
${
|
|
11902
|
-
${
|
|
11903
|
-
${
|
|
11904
|
-
${
|
|
11905
|
-
${
|
|
11906
|
-
|
|
11907
|
-
${
|
|
11908
|
-
${
|
|
11909
|
-
${
|
|
11910
|
-
${
|
|
11911
|
-
${
|
|
11912
|
-
${
|
|
11913
|
-
${
|
|
11914
|
-
${
|
|
11915
|
-
|
|
11916
|
-
${
|
|
11917
|
-
${
|
|
11918
|
-
${
|
|
11919
|
-
${
|
|
11920
|
-
|
|
11921
|
-
${
|
|
11922
|
-
|
|
11923
|
-
|
|
11924
|
-
${
|
|
11925
|
-
|
|
11926
|
-
|
|
11927
|
-
${
|
|
11928
|
-
${
|
|
11929
|
-
|
|
11930
|
-
${
|
|
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();
|