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