lynxprompt 0.2.1 → 0.3.0
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 +571 -357
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import 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
|
}
|
|
@@ -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 access3(
|
|
1596
|
+
await access3(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,23 +1947,23 @@ 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
|
|
1964
|
+
import chalk8 from "chalk";
|
|
1965
|
+
import prompts4 from "prompts";
|
|
1966
|
+
import ora7 from "ora";
|
|
1755
1967
|
import { writeFile as writeFile4, mkdir as mkdir4, access as access5 } from "fs/promises";
|
|
1756
1968
|
import { join as join6, dirname as dirname4 } from "path";
|
|
1757
1969
|
|
|
@@ -2117,17 +2329,17 @@ var BOUNDARY_PRESETS = [
|
|
|
2117
2329
|
];
|
|
2118
2330
|
async function wizardCommand(options) {
|
|
2119
2331
|
console.log();
|
|
2120
|
-
console.log(
|
|
2121
|
-
console.log(
|
|
2332
|
+
console.log(chalk8.cyan("\u{1F431} LynxPrompt Wizard"));
|
|
2333
|
+
console.log(chalk8.gray("Generate AI IDE configuration in seconds"));
|
|
2122
2334
|
console.log();
|
|
2123
2335
|
const detected = await detectProject(process.cwd());
|
|
2124
2336
|
if (detected) {
|
|
2125
|
-
console.log(
|
|
2126
|
-
if (detected.name) console.log(
|
|
2127
|
-
if (detected.stack.length > 0) console.log(
|
|
2128
|
-
if (detected.packageManager) console.log(
|
|
2129
|
-
if (detected.commands.build) console.log(
|
|
2130
|
-
if (detected.commands.test) console.log(
|
|
2337
|
+
console.log(chalk8.green("\u2713 Detected project:"));
|
|
2338
|
+
if (detected.name) console.log(chalk8.gray(` Name: ${detected.name}`));
|
|
2339
|
+
if (detected.stack.length > 0) console.log(chalk8.gray(` Stack: ${detected.stack.join(", ")}`));
|
|
2340
|
+
if (detected.packageManager) console.log(chalk8.gray(` Package manager: ${detected.packageManager}`));
|
|
2341
|
+
if (detected.commands.build) console.log(chalk8.gray(` Build: ${detected.commands.build}`));
|
|
2342
|
+
if (detected.commands.test) console.log(chalk8.gray(` Test: ${detected.commands.test}`));
|
|
2131
2343
|
console.log();
|
|
2132
2344
|
}
|
|
2133
2345
|
let config2;
|
|
@@ -2152,12 +2364,12 @@ async function wizardCommand(options) {
|
|
|
2152
2364
|
} else {
|
|
2153
2365
|
config2 = await runInteractiveWizard(options, detected);
|
|
2154
2366
|
}
|
|
2155
|
-
const spinner =
|
|
2367
|
+
const spinner = ora7("Generating configuration...").start();
|
|
2156
2368
|
try {
|
|
2157
2369
|
const files = generateConfig(config2);
|
|
2158
2370
|
spinner.stop();
|
|
2159
2371
|
console.log();
|
|
2160
|
-
console.log(
|
|
2372
|
+
console.log(chalk8.green("\u2705 Generated:"));
|
|
2161
2373
|
for (const [filename, content] of Object.entries(files)) {
|
|
2162
2374
|
const outputPath = join6(process.cwd(), filename);
|
|
2163
2375
|
let exists = false;
|
|
@@ -2167,14 +2379,14 @@ async function wizardCommand(options) {
|
|
|
2167
2379
|
} catch {
|
|
2168
2380
|
}
|
|
2169
2381
|
if (exists && !options.yes) {
|
|
2170
|
-
const response = await
|
|
2382
|
+
const response = await prompts4({
|
|
2171
2383
|
type: "confirm",
|
|
2172
2384
|
name: "overwrite",
|
|
2173
2385
|
message: `${filename} already exists. Overwrite?`,
|
|
2174
2386
|
initial: false
|
|
2175
2387
|
});
|
|
2176
2388
|
if (!response.overwrite) {
|
|
2177
|
-
console.log(
|
|
2389
|
+
console.log(chalk8.yellow(` Skipped: ${filename}`));
|
|
2178
2390
|
continue;
|
|
2179
2391
|
}
|
|
2180
2392
|
}
|
|
@@ -2183,23 +2395,23 @@ async function wizardCommand(options) {
|
|
|
2183
2395
|
await mkdir4(dir, { recursive: true });
|
|
2184
2396
|
}
|
|
2185
2397
|
await writeFile4(outputPath, content, "utf-8");
|
|
2186
|
-
console.log(` ${
|
|
2398
|
+
console.log(` ${chalk8.cyan(filename)}`);
|
|
2187
2399
|
}
|
|
2188
2400
|
console.log();
|
|
2189
|
-
console.log(
|
|
2401
|
+
console.log(chalk8.gray("Your AI assistant will now follow these instructions."));
|
|
2190
2402
|
console.log();
|
|
2191
|
-
console.log(
|
|
2192
|
-
console.log(
|
|
2193
|
-
console.log(
|
|
2194
|
-
console.log(
|
|
2403
|
+
console.log(chalk8.gray("Tips:"));
|
|
2404
|
+
console.log(chalk8.gray(" \u2022 Edit the generated file anytime to customize rules"));
|
|
2405
|
+
console.log(chalk8.gray(" \u2022 Run 'lynxp wizard' again to regenerate"));
|
|
2406
|
+
console.log(chalk8.gray(" \u2022 Run 'lynxp check' to validate your configuration"));
|
|
2195
2407
|
console.log();
|
|
2196
2408
|
} catch (error) {
|
|
2197
2409
|
spinner.fail("Failed to generate files");
|
|
2198
|
-
console.error(
|
|
2410
|
+
console.error(chalk8.red("\n\u2717 An error occurred while generating configuration files."));
|
|
2199
2411
|
if (error instanceof Error) {
|
|
2200
|
-
console.error(
|
|
2412
|
+
console.error(chalk8.gray(` ${error.message}`));
|
|
2201
2413
|
}
|
|
2202
|
-
console.error(
|
|
2414
|
+
console.error(chalk8.gray("\nTry running with --yes flag for default settings."));
|
|
2203
2415
|
process.exit(1);
|
|
2204
2416
|
}
|
|
2205
2417
|
}
|
|
@@ -2209,12 +2421,12 @@ async function runInteractiveWizard(options, detected) {
|
|
|
2209
2421
|
if (options.format) {
|
|
2210
2422
|
platforms = options.format.split(",").map((f) => f.trim());
|
|
2211
2423
|
} else {
|
|
2212
|
-
const formatResponse = await
|
|
2424
|
+
const formatResponse = await prompts4({
|
|
2213
2425
|
type: "select",
|
|
2214
2426
|
name: "format",
|
|
2215
2427
|
message: "Select output format:",
|
|
2216
2428
|
choices: OUTPUT_FORMATS.map((f) => ({
|
|
2217
|
-
title: f.recommended ? `${f.title} ${
|
|
2429
|
+
title: f.recommended ? `${f.title} ${chalk8.green("(recommended)")}` : f.title,
|
|
2218
2430
|
value: f.value,
|
|
2219
2431
|
description: f.description
|
|
2220
2432
|
})),
|
|
@@ -2222,7 +2434,7 @@ async function runInteractiveWizard(options, detected) {
|
|
|
2222
2434
|
// AGENTS.md is default
|
|
2223
2435
|
});
|
|
2224
2436
|
if (formatResponse.format === "multiple") {
|
|
2225
|
-
const platformResponse = await
|
|
2437
|
+
const platformResponse = await prompts4({
|
|
2226
2438
|
type: "multiselect",
|
|
2227
2439
|
name: "platforms",
|
|
2228
2440
|
message: "Select AI editors:",
|
|
@@ -2236,14 +2448,14 @@ async function runInteractiveWizard(options, detected) {
|
|
|
2236
2448
|
}
|
|
2237
2449
|
}
|
|
2238
2450
|
answers.platforms = platforms;
|
|
2239
|
-
const nameResponse = await
|
|
2451
|
+
const nameResponse = await prompts4({
|
|
2240
2452
|
type: "text",
|
|
2241
2453
|
name: "name",
|
|
2242
2454
|
message: "Project name:",
|
|
2243
2455
|
initial: options.name || detected?.name || "my-project"
|
|
2244
2456
|
});
|
|
2245
2457
|
answers.name = nameResponse.name || "my-project";
|
|
2246
|
-
const descResponse = await
|
|
2458
|
+
const descResponse = await prompts4({
|
|
2247
2459
|
type: "text",
|
|
2248
2460
|
name: "description",
|
|
2249
2461
|
message: "Brief description (optional):",
|
|
@@ -2252,7 +2464,7 @@ async function runInteractiveWizard(options, detected) {
|
|
|
2252
2464
|
answers.description = descResponse.description || "";
|
|
2253
2465
|
const allStackOptions = [...TECH_STACKS, ...FRAMEWORKS];
|
|
2254
2466
|
const detectedStackSet = new Set(detected?.stack || []);
|
|
2255
|
-
const stackResponse = await
|
|
2467
|
+
const stackResponse = await prompts4({
|
|
2256
2468
|
type: "multiselect",
|
|
2257
2469
|
name: "stack",
|
|
2258
2470
|
message: "Tech stack:",
|
|
@@ -2264,7 +2476,7 @@ async function runInteractiveWizard(options, detected) {
|
|
|
2264
2476
|
hint: "- Space to select, Enter to confirm"
|
|
2265
2477
|
});
|
|
2266
2478
|
answers.stack = stackResponse.stack || [];
|
|
2267
|
-
const personaResponse = await
|
|
2479
|
+
const personaResponse = await prompts4({
|
|
2268
2480
|
type: "select",
|
|
2269
2481
|
name: "persona",
|
|
2270
2482
|
message: "AI persona:",
|
|
@@ -2273,7 +2485,7 @@ async function runInteractiveWizard(options, detected) {
|
|
|
2273
2485
|
// Full-stack by default
|
|
2274
2486
|
});
|
|
2275
2487
|
if (personaResponse.persona === "custom") {
|
|
2276
|
-
const customPersona = await
|
|
2488
|
+
const customPersona = await prompts4({
|
|
2277
2489
|
type: "text",
|
|
2278
2490
|
name: "value",
|
|
2279
2491
|
message: "Describe the custom persona:"
|
|
@@ -2282,7 +2494,7 @@ async function runInteractiveWizard(options, detected) {
|
|
|
2282
2494
|
} else {
|
|
2283
2495
|
answers.persona = personaResponse.persona || "fullstack";
|
|
2284
2496
|
}
|
|
2285
|
-
const boundaryResponse = await
|
|
2497
|
+
const boundaryResponse = await prompts4({
|
|
2286
2498
|
type: "select",
|
|
2287
2499
|
name: "boundaries",
|
|
2288
2500
|
message: "AI boundaries:",
|
|
@@ -2293,19 +2505,19 @@ async function runInteractiveWizard(options, detected) {
|
|
|
2293
2505
|
answers.boundaries = boundaryResponse.boundaries || "standard";
|
|
2294
2506
|
if (detected?.commands && Object.keys(detected.commands).length > 0) {
|
|
2295
2507
|
console.log();
|
|
2296
|
-
console.log(
|
|
2297
|
-
if (detected.commands.build) console.log(
|
|
2298
|
-
if (detected.commands.test) console.log(
|
|
2299
|
-
if (detected.commands.lint) console.log(
|
|
2300
|
-
if (detected.commands.dev) console.log(
|
|
2301
|
-
const editCommands = await
|
|
2508
|
+
console.log(chalk8.gray("Auto-detected commands:"));
|
|
2509
|
+
if (detected.commands.build) console.log(chalk8.gray(` Build: ${detected.commands.build}`));
|
|
2510
|
+
if (detected.commands.test) console.log(chalk8.gray(` Test: ${detected.commands.test}`));
|
|
2511
|
+
if (detected.commands.lint) console.log(chalk8.gray(` Lint: ${detected.commands.lint}`));
|
|
2512
|
+
if (detected.commands.dev) console.log(chalk8.gray(` Dev: ${detected.commands.dev}`));
|
|
2513
|
+
const editCommands = await prompts4({
|
|
2302
2514
|
type: "confirm",
|
|
2303
2515
|
name: "edit",
|
|
2304
2516
|
message: "Edit commands?",
|
|
2305
2517
|
initial: false
|
|
2306
2518
|
});
|
|
2307
2519
|
if (editCommands.edit) {
|
|
2308
|
-
const commandsResponse = await
|
|
2520
|
+
const commandsResponse = await prompts4([
|
|
2309
2521
|
{ type: "text", name: "build", message: "Build:", initial: detected.commands.build },
|
|
2310
2522
|
{ type: "text", name: "test", message: "Test:", initial: detected.commands.test },
|
|
2311
2523
|
{ type: "text", name: "lint", message: "Lint:", initial: detected.commands.lint },
|
|
@@ -2330,52 +2542,52 @@ async function runInteractiveWizard(options, detected) {
|
|
|
2330
2542
|
}
|
|
2331
2543
|
|
|
2332
2544
|
// src/commands/search.ts
|
|
2333
|
-
import
|
|
2334
|
-
import
|
|
2545
|
+
import chalk9 from "chalk";
|
|
2546
|
+
import ora8 from "ora";
|
|
2335
2547
|
async function searchCommand(query, options) {
|
|
2336
|
-
const spinner =
|
|
2548
|
+
const spinner = ora8(`Searching for "${query}"...`).start();
|
|
2337
2549
|
try {
|
|
2338
2550
|
const limit = parseInt(options.limit, 10) || 20;
|
|
2339
2551
|
const { templates, total, hasMore } = await api.searchBlueprints(query, limit);
|
|
2340
2552
|
spinner.stop();
|
|
2341
2553
|
if (templates.length === 0) {
|
|
2342
2554
|
console.log();
|
|
2343
|
-
console.log(
|
|
2344
|
-
console.log(
|
|
2555
|
+
console.log(chalk9.yellow(`No blueprints found for "${query}".`));
|
|
2556
|
+
console.log(chalk9.gray("Try different keywords or browse at https://lynxprompt.com/blueprints"));
|
|
2345
2557
|
return;
|
|
2346
2558
|
}
|
|
2347
2559
|
console.log();
|
|
2348
|
-
console.log(
|
|
2560
|
+
console.log(chalk9.cyan(`\u{1F50D} Search Results for "${query}" (${total} found)`));
|
|
2349
2561
|
console.log();
|
|
2350
2562
|
for (const result of templates) {
|
|
2351
2563
|
printSearchResult(result);
|
|
2352
2564
|
}
|
|
2353
2565
|
if (hasMore) {
|
|
2354
|
-
console.log(
|
|
2566
|
+
console.log(chalk9.gray(`Showing ${templates.length} of ${total}. Use --limit to see more.`));
|
|
2355
2567
|
}
|
|
2356
2568
|
console.log();
|
|
2357
|
-
console.log(
|
|
2569
|
+
console.log(chalk9.gray("Use 'lynxprompt pull <id>' to download a blueprint."));
|
|
2358
2570
|
} catch (error) {
|
|
2359
2571
|
spinner.fail("Search failed");
|
|
2360
2572
|
handleApiError3(error);
|
|
2361
2573
|
}
|
|
2362
2574
|
}
|
|
2363
2575
|
function printSearchResult(result) {
|
|
2364
|
-
const priceInfo = result.price ?
|
|
2365
|
-
const officialBadge = result.isOfficial ?
|
|
2366
|
-
console.log(` ${
|
|
2367
|
-
console.log(` ${
|
|
2576
|
+
const priceInfo = result.price ? chalk9.yellow(`\u20AC${(result.price / 100).toFixed(2)}`) : chalk9.green("Free");
|
|
2577
|
+
const officialBadge = result.isOfficial ? chalk9.magenta(" \u2605 Official") : "";
|
|
2578
|
+
console.log(` ${chalk9.bold(result.name)}${officialBadge}`);
|
|
2579
|
+
console.log(` ${chalk9.cyan(result.id)} \u2022 ${priceInfo}`);
|
|
2368
2580
|
if (result.description) {
|
|
2369
|
-
console.log(` ${
|
|
2581
|
+
console.log(` ${chalk9.gray(truncate2(result.description, 60))}`);
|
|
2370
2582
|
}
|
|
2371
|
-
console.log(` ${
|
|
2583
|
+
console.log(` ${chalk9.gray(`by ${result.author}`)} \u2022 ${chalk9.gray(`\u2193${result.downloads}`)} ${chalk9.gray(`\u2665${result.likes}`)}`);
|
|
2372
2584
|
if (result.tags && result.tags.length > 0) {
|
|
2373
2585
|
console.log(` ${formatTags2(result.tags)}`);
|
|
2374
2586
|
}
|
|
2375
2587
|
console.log();
|
|
2376
2588
|
}
|
|
2377
2589
|
function formatTags2(tags) {
|
|
2378
|
-
return tags.slice(0, 4).map((t) =>
|
|
2590
|
+
return tags.slice(0, 4).map((t) => chalk9.gray(`#${t}`)).join(" ");
|
|
2379
2591
|
}
|
|
2380
2592
|
function truncate2(str, maxLength) {
|
|
2381
2593
|
if (str.length <= maxLength) return str;
|
|
@@ -2383,15 +2595,15 @@ function truncate2(str, maxLength) {
|
|
|
2383
2595
|
}
|
|
2384
2596
|
function handleApiError3(error) {
|
|
2385
2597
|
if (error instanceof ApiRequestError) {
|
|
2386
|
-
console.error(
|
|
2598
|
+
console.error(chalk9.red(`Error: ${error.message}`));
|
|
2387
2599
|
} else {
|
|
2388
|
-
console.error(
|
|
2600
|
+
console.error(chalk9.red("An unexpected error occurred."));
|
|
2389
2601
|
}
|
|
2390
2602
|
process.exit(1);
|
|
2391
2603
|
}
|
|
2392
2604
|
|
|
2393
2605
|
// src/commands/status.ts
|
|
2394
|
-
import
|
|
2606
|
+
import chalk10 from "chalk";
|
|
2395
2607
|
import { access as access6, readFile as readFile6, readdir as readdir3 } from "fs/promises";
|
|
2396
2608
|
import { join as join7 } from "path";
|
|
2397
2609
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -2413,12 +2625,12 @@ var CONFIG_DIRS = [
|
|
|
2413
2625
|
async function statusCommand() {
|
|
2414
2626
|
const cwd = process.cwd();
|
|
2415
2627
|
console.log();
|
|
2416
|
-
console.log(
|
|
2417
|
-
console.log(
|
|
2628
|
+
console.log(chalk10.cyan("\u{1F431} LynxPrompt Status"));
|
|
2629
|
+
console.log(chalk10.gray(` Directory: ${cwd}`));
|
|
2418
2630
|
console.log();
|
|
2419
2631
|
const lynxpromptExists = existsSync4(join7(cwd, ".lynxprompt"));
|
|
2420
2632
|
if (lynxpromptExists) {
|
|
2421
|
-
console.log(
|
|
2633
|
+
console.log(chalk10.green("\u2713 LynxPrompt initialized"));
|
|
2422
2634
|
const configPath = join7(cwd, ".lynxprompt/conf.yml");
|
|
2423
2635
|
if (existsSync4(configPath)) {
|
|
2424
2636
|
try {
|
|
@@ -2426,7 +2638,7 @@ async function statusCommand() {
|
|
|
2426
2638
|
const { parse: parse5 } = await import("yaml");
|
|
2427
2639
|
const config2 = parse5(content);
|
|
2428
2640
|
if (config2?.exporters?.length > 0) {
|
|
2429
|
-
console.log(
|
|
2641
|
+
console.log(chalk10.gray(` Exporters: ${config2.exporters.join(", ")}`));
|
|
2430
2642
|
}
|
|
2431
2643
|
} catch {
|
|
2432
2644
|
}
|
|
@@ -2435,31 +2647,31 @@ async function statusCommand() {
|
|
|
2435
2647
|
}
|
|
2436
2648
|
const trackedStatus = await checkSyncStatus(cwd);
|
|
2437
2649
|
if (trackedStatus.length > 0) {
|
|
2438
|
-
console.log(
|
|
2650
|
+
console.log(chalk10.cyan("\u{1F4E6} Tracked Blueprints"));
|
|
2439
2651
|
console.log();
|
|
2440
2652
|
for (const { blueprint, localModified, fileExists: fileExists2 } of trackedStatus) {
|
|
2441
|
-
const statusIcon = !fileExists2 ?
|
|
2653
|
+
const statusIcon = !fileExists2 ? chalk10.red("\u2717") : localModified ? chalk10.yellow("\u25CF") : chalk10.green("\u2713");
|
|
2442
2654
|
const sourceLabel = {
|
|
2443
|
-
marketplace:
|
|
2444
|
-
team:
|
|
2445
|
-
private:
|
|
2446
|
-
local:
|
|
2655
|
+
marketplace: chalk10.gray("[marketplace]"),
|
|
2656
|
+
team: chalk10.blue("[team]"),
|
|
2657
|
+
private: chalk10.green("[private]"),
|
|
2658
|
+
local: chalk10.gray("[local]")
|
|
2447
2659
|
}[blueprint.source];
|
|
2448
|
-
console.log(` ${statusIcon} ${
|
|
2449
|
-
console.log(` ${
|
|
2660
|
+
console.log(` ${statusIcon} ${chalk10.bold(blueprint.file)} ${sourceLabel}`);
|
|
2661
|
+
console.log(` ${chalk10.gray(`ID: ${blueprint.id} \u2022 ${blueprint.name}`)}`);
|
|
2450
2662
|
if (!fileExists2) {
|
|
2451
|
-
console.log(
|
|
2663
|
+
console.log(chalk10.red(` \u26A0 File missing - run 'lynxp pull ${blueprint.id}' to restore`));
|
|
2452
2664
|
} else if (localModified) {
|
|
2453
2665
|
if (blueprint.source === "marketplace") {
|
|
2454
|
-
console.log(
|
|
2666
|
+
console.log(chalk10.yellow(` \u26A0 Local changes (marketplace = read-only, won't sync back)`));
|
|
2455
2667
|
} else {
|
|
2456
|
-
console.log(
|
|
2668
|
+
console.log(chalk10.yellow(` \u26A0 Local changes - run 'lynxp push ${blueprint.file}' to sync`));
|
|
2457
2669
|
}
|
|
2458
2670
|
}
|
|
2459
2671
|
console.log();
|
|
2460
2672
|
}
|
|
2461
2673
|
}
|
|
2462
|
-
console.log(
|
|
2674
|
+
console.log(chalk10.cyan("\u{1F4C4} AI Config Files"));
|
|
2463
2675
|
console.log();
|
|
2464
2676
|
let foundAny = false;
|
|
2465
2677
|
for (const config2 of CONFIG_FILES) {
|
|
@@ -2471,13 +2683,13 @@ async function statusCommand() {
|
|
|
2471
2683
|
const size = formatBytes(content.length);
|
|
2472
2684
|
foundAny = true;
|
|
2473
2685
|
const tracked = trackedStatus.find((t) => t.blueprint.file === config2.path);
|
|
2474
|
-
const trackedLabel = tracked ?
|
|
2475
|
-
console.log(` ${
|
|
2476
|
-
console.log(` ${
|
|
2477
|
-
console.log(` ${
|
|
2686
|
+
const trackedLabel = tracked ? chalk10.cyan(" (tracked)") : "";
|
|
2687
|
+
console.log(` ${chalk10.green("\u2713")} ${chalk10.bold(config2.name)}${trackedLabel}`);
|
|
2688
|
+
console.log(` ${chalk10.gray(`Platform: ${config2.platform}`)}`);
|
|
2689
|
+
console.log(` ${chalk10.gray(`Size: ${size} (${lines} lines)`)}`);
|
|
2478
2690
|
const preview = getPreview(content);
|
|
2479
2691
|
if (preview) {
|
|
2480
|
-
console.log(` ${
|
|
2692
|
+
console.log(` ${chalk10.gray(`Preview: ${preview}`)}`);
|
|
2481
2693
|
}
|
|
2482
2694
|
console.log();
|
|
2483
2695
|
} catch {
|
|
@@ -2491,14 +2703,14 @@ async function statusCommand() {
|
|
|
2491
2703
|
const ruleFiles = files.filter((f) => f.endsWith(".md") || f.endsWith(".mdc"));
|
|
2492
2704
|
if (ruleFiles.length > 0) {
|
|
2493
2705
|
foundAny = true;
|
|
2494
|
-
console.log(` ${
|
|
2495
|
-
console.log(` ${
|
|
2496
|
-
console.log(` ${
|
|
2706
|
+
console.log(` ${chalk10.green("\u2713")} ${chalk10.bold(config2.name)}`);
|
|
2707
|
+
console.log(` ${chalk10.gray(`Platform: ${config2.platform}`)}`);
|
|
2708
|
+
console.log(` ${chalk10.gray(`Rules: ${ruleFiles.length} file${ruleFiles.length === 1 ? "" : "s"}`)}`);
|
|
2497
2709
|
for (const file of ruleFiles.slice(0, 3)) {
|
|
2498
|
-
console.log(` ${
|
|
2710
|
+
console.log(` ${chalk10.gray(` \u2022 ${file}`)}`);
|
|
2499
2711
|
}
|
|
2500
2712
|
if (ruleFiles.length > 3) {
|
|
2501
|
-
console.log(` ${
|
|
2713
|
+
console.log(` ${chalk10.gray(` ... and ${ruleFiles.length - 3} more`)}`);
|
|
2502
2714
|
}
|
|
2503
2715
|
console.log();
|
|
2504
2716
|
}
|
|
@@ -2507,17 +2719,17 @@ async function statusCommand() {
|
|
|
2507
2719
|
}
|
|
2508
2720
|
}
|
|
2509
2721
|
if (!foundAny) {
|
|
2510
|
-
console.log(
|
|
2722
|
+
console.log(chalk10.yellow(" No AI configuration files found."));
|
|
2511
2723
|
console.log();
|
|
2512
|
-
console.log(
|
|
2513
|
-
console.log(
|
|
2514
|
-
console.log(
|
|
2515
|
-
console.log(
|
|
2724
|
+
console.log(chalk10.gray(" Get started:"));
|
|
2725
|
+
console.log(chalk10.gray(" lynxp wizard Generate a configuration"));
|
|
2726
|
+
console.log(chalk10.gray(" lynxp pull <id> Download from marketplace"));
|
|
2727
|
+
console.log(chalk10.gray(" lynxp search <query> Search for blueprints"));
|
|
2516
2728
|
} else {
|
|
2517
|
-
console.log(
|
|
2518
|
-
console.log(
|
|
2519
|
-
console.log(
|
|
2520
|
-
console.log(
|
|
2729
|
+
console.log(chalk10.gray("Commands:"));
|
|
2730
|
+
console.log(chalk10.gray(" lynxp wizard Regenerate configuration"));
|
|
2731
|
+
console.log(chalk10.gray(" lynxp check Validate files"));
|
|
2732
|
+
console.log(chalk10.gray(" lynxp link --list Show tracked blueprints"));
|
|
2521
2733
|
}
|
|
2522
2734
|
console.log();
|
|
2523
2735
|
}
|
|
@@ -2542,9 +2754,9 @@ function formatBytes(bytes) {
|
|
|
2542
2754
|
}
|
|
2543
2755
|
|
|
2544
2756
|
// src/commands/sync.ts
|
|
2545
|
-
import
|
|
2546
|
-
import
|
|
2547
|
-
import
|
|
2757
|
+
import chalk11 from "chalk";
|
|
2758
|
+
import ora9 from "ora";
|
|
2759
|
+
import prompts5 from "prompts";
|
|
2548
2760
|
import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5, readdir as readdir4 } from "fs/promises";
|
|
2549
2761
|
import { join as join8, dirname as dirname5 } from "path";
|
|
2550
2762
|
import { existsSync as existsSync5 } from "fs";
|
|
@@ -2553,17 +2765,17 @@ var CONFIG_FILE = ".lynxprompt/conf.yml";
|
|
|
2553
2765
|
var RULES_DIR = ".lynxprompt/rules";
|
|
2554
2766
|
async function syncCommand(options = {}) {
|
|
2555
2767
|
console.log();
|
|
2556
|
-
console.log(
|
|
2768
|
+
console.log(chalk11.cyan("\u{1F431} LynxPrompt Sync"));
|
|
2557
2769
|
console.log();
|
|
2558
2770
|
const cwd = process.cwd();
|
|
2559
2771
|
const configPath = join8(cwd, CONFIG_FILE);
|
|
2560
2772
|
if (!existsSync5(configPath)) {
|
|
2561
|
-
console.log(
|
|
2773
|
+
console.log(chalk11.yellow("LynxPrompt is not initialized in this project."));
|
|
2562
2774
|
console.log();
|
|
2563
|
-
console.log(
|
|
2775
|
+
console.log(chalk11.gray("Run 'lynxp init' first to set up LynxPrompt."));
|
|
2564
2776
|
return;
|
|
2565
2777
|
}
|
|
2566
|
-
const spinner =
|
|
2778
|
+
const spinner = ora9("Loading configuration...").start();
|
|
2567
2779
|
let config2;
|
|
2568
2780
|
try {
|
|
2569
2781
|
const configContent = await readFile7(configPath, "utf-8");
|
|
@@ -2571,13 +2783,13 @@ async function syncCommand(options = {}) {
|
|
|
2571
2783
|
spinner.succeed("Configuration loaded");
|
|
2572
2784
|
} catch (error) {
|
|
2573
2785
|
spinner.fail("Failed to load configuration");
|
|
2574
|
-
console.log(
|
|
2786
|
+
console.log(chalk11.red("Could not parse .lynxprompt/conf.yml"));
|
|
2575
2787
|
return;
|
|
2576
2788
|
}
|
|
2577
2789
|
if (!config2.exporters || config2.exporters.length === 0) {
|
|
2578
|
-
console.log(
|
|
2790
|
+
console.log(chalk11.yellow("No exporters configured."));
|
|
2579
2791
|
console.log();
|
|
2580
|
-
console.log(
|
|
2792
|
+
console.log(chalk11.gray("Add exporters to .lynxprompt/conf.yml or run 'lynxp agents enable <agent>'"));
|
|
2581
2793
|
return;
|
|
2582
2794
|
}
|
|
2583
2795
|
const validExporters = [];
|
|
@@ -2591,51 +2803,51 @@ async function syncCommand(options = {}) {
|
|
|
2591
2803
|
}
|
|
2592
2804
|
}
|
|
2593
2805
|
if (invalidExporters.length > 0) {
|
|
2594
|
-
console.log(
|
|
2806
|
+
console.log(chalk11.yellow(`Unknown exporters: ${invalidExporters.join(", ")}`));
|
|
2595
2807
|
}
|
|
2596
2808
|
if (validExporters.length === 0) {
|
|
2597
|
-
console.log(
|
|
2809
|
+
console.log(chalk11.red("No valid exporters configured."));
|
|
2598
2810
|
return;
|
|
2599
2811
|
}
|
|
2600
|
-
console.log(
|
|
2812
|
+
console.log(chalk11.gray(`Exporters: ${validExporters.map((e) => e.name).join(", ")}`));
|
|
2601
2813
|
console.log();
|
|
2602
2814
|
const rulesPath = join8(cwd, RULES_DIR);
|
|
2603
2815
|
if (!existsSync5(rulesPath)) {
|
|
2604
|
-
console.log(
|
|
2605
|
-
console.log(
|
|
2816
|
+
console.log(chalk11.yellow("No rules found."));
|
|
2817
|
+
console.log(chalk11.gray(`Create rules in ${RULES_DIR}/ to sync them.`));
|
|
2606
2818
|
return;
|
|
2607
2819
|
}
|
|
2608
2820
|
const rulesContent = await loadRules(rulesPath);
|
|
2609
2821
|
if (!rulesContent) {
|
|
2610
|
-
console.log(
|
|
2822
|
+
console.log(chalk11.yellow("No rule files found in .lynxprompt/rules/"));
|
|
2611
2823
|
return;
|
|
2612
2824
|
}
|
|
2613
|
-
console.log(
|
|
2825
|
+
console.log(chalk11.gray(`Loaded ${rulesContent.fileCount} rule file${rulesContent.fileCount === 1 ? "" : "s"}`));
|
|
2614
2826
|
console.log();
|
|
2615
2827
|
if (options.dryRun) {
|
|
2616
|
-
console.log(
|
|
2828
|
+
console.log(chalk11.cyan("Dry run - no files will be written"));
|
|
2617
2829
|
console.log();
|
|
2618
2830
|
console.log("Would write:");
|
|
2619
2831
|
for (const exporter of validExporters) {
|
|
2620
|
-
console.log(
|
|
2832
|
+
console.log(chalk11.gray(` ${exporter.output}`));
|
|
2621
2833
|
}
|
|
2622
2834
|
console.log();
|
|
2623
2835
|
return;
|
|
2624
2836
|
}
|
|
2625
2837
|
if (!options.force) {
|
|
2626
|
-
const { confirm } = await
|
|
2838
|
+
const { confirm } = await prompts5({
|
|
2627
2839
|
type: "confirm",
|
|
2628
2840
|
name: "confirm",
|
|
2629
2841
|
message: `Sync to ${validExporters.length} agent${validExporters.length === 1 ? "" : "s"}?`,
|
|
2630
2842
|
initial: true
|
|
2631
2843
|
});
|
|
2632
2844
|
if (!confirm) {
|
|
2633
|
-
console.log(
|
|
2845
|
+
console.log(chalk11.gray("Cancelled."));
|
|
2634
2846
|
return;
|
|
2635
2847
|
}
|
|
2636
2848
|
}
|
|
2637
2849
|
const result = { written: [], skipped: [], errors: [] };
|
|
2638
|
-
const syncSpinner =
|
|
2850
|
+
const syncSpinner = ora9("Syncing rules...").start();
|
|
2639
2851
|
for (const exporter of validExporters) {
|
|
2640
2852
|
try {
|
|
2641
2853
|
await syncToAgent(cwd, exporter, rulesContent.combined);
|
|
@@ -2646,16 +2858,16 @@ async function syncCommand(options = {}) {
|
|
|
2646
2858
|
}
|
|
2647
2859
|
syncSpinner.stop();
|
|
2648
2860
|
if (result.written.length > 0) {
|
|
2649
|
-
console.log(
|
|
2861
|
+
console.log(chalk11.green(`\u2713 Synced to ${result.written.length} agent${result.written.length === 1 ? "" : "s"}:`));
|
|
2650
2862
|
for (const file of result.written) {
|
|
2651
|
-
console.log(
|
|
2863
|
+
console.log(chalk11.gray(` ${file}`));
|
|
2652
2864
|
}
|
|
2653
2865
|
}
|
|
2654
2866
|
if (result.errors.length > 0) {
|
|
2655
2867
|
console.log();
|
|
2656
|
-
console.log(
|
|
2868
|
+
console.log(chalk11.red("Errors:"));
|
|
2657
2869
|
for (const error of result.errors) {
|
|
2658
|
-
console.log(
|
|
2870
|
+
console.log(chalk11.red(` ${error}`));
|
|
2659
2871
|
}
|
|
2660
2872
|
}
|
|
2661
2873
|
console.log();
|
|
@@ -2754,8 +2966,8 @@ function formatAsJson(content, agent) {
|
|
|
2754
2966
|
}
|
|
2755
2967
|
|
|
2756
2968
|
// src/commands/agents.ts
|
|
2757
|
-
import
|
|
2758
|
-
import
|
|
2969
|
+
import chalk12 from "chalk";
|
|
2970
|
+
import prompts6 from "prompts";
|
|
2759
2971
|
import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
|
|
2760
2972
|
import { join as join9 } from "path";
|
|
2761
2973
|
import { existsSync as existsSync6 } from "fs";
|
|
@@ -2780,18 +2992,18 @@ async function agentsCommand(action, agentId, options = {}) {
|
|
|
2780
2992
|
}
|
|
2781
2993
|
}
|
|
2782
2994
|
async function listAgents() {
|
|
2783
|
-
console.log(
|
|
2995
|
+
console.log(chalk12.cyan("\u{1F431} LynxPrompt Agents"));
|
|
2784
2996
|
console.log();
|
|
2785
2997
|
const config2 = await loadConfig();
|
|
2786
2998
|
const enabledSet = new Set(config2?.exporters ?? []);
|
|
2787
2999
|
const detection = detectAgents();
|
|
2788
3000
|
if (enabledSet.size > 0) {
|
|
2789
|
-
console.log(
|
|
3001
|
+
console.log(chalk12.green("Enabled:"));
|
|
2790
3002
|
for (const id of enabledSet) {
|
|
2791
3003
|
const agent = getAgent(id);
|
|
2792
3004
|
const detected = detection.detected.find((d) => d.agent.id === id);
|
|
2793
|
-
const status = detected ?
|
|
2794
|
-
console.log(` ${
|
|
3005
|
+
const status = detected ? chalk12.gray("(detected)") : "";
|
|
3006
|
+
console.log(` ${chalk12.green("\u2713")} ${agent?.name ?? id} ${status}`);
|
|
2795
3007
|
}
|
|
2796
3008
|
console.log();
|
|
2797
3009
|
}
|
|
@@ -2799,10 +3011,10 @@ async function listAgents() {
|
|
|
2799
3011
|
(d) => !enabledSet.has(d.agent.id)
|
|
2800
3012
|
);
|
|
2801
3013
|
if (detectedNotEnabled.length > 0) {
|
|
2802
|
-
console.log(
|
|
3014
|
+
console.log(chalk12.yellow("Detected (not enabled):"));
|
|
2803
3015
|
for (const detected of detectedNotEnabled) {
|
|
2804
|
-
const rules = detected.ruleCount > 0 ?
|
|
2805
|
-
console.log(` ${
|
|
3016
|
+
const rules = detected.ruleCount > 0 ? chalk12.gray(` (${detected.ruleCount} rules)`) : "";
|
|
3017
|
+
console.log(` ${chalk12.yellow("\u25CB")} ${detected.agent.name}${rules}`);
|
|
2806
3018
|
}
|
|
2807
3019
|
console.log();
|
|
2808
3020
|
}
|
|
@@ -2810,30 +3022,30 @@ async function listAgents() {
|
|
|
2810
3022
|
(a) => !enabledSet.has(a.id) && !detectedNotEnabled.some((d) => d.agent.id === a.id)
|
|
2811
3023
|
);
|
|
2812
3024
|
if (popular.length > 0) {
|
|
2813
|
-
console.log(
|
|
3025
|
+
console.log(chalk12.gray("Popular (available):"));
|
|
2814
3026
|
for (const agent of popular) {
|
|
2815
|
-
console.log(` ${
|
|
3027
|
+
console.log(` ${chalk12.gray("-")} ${agent.name} - ${agent.description}`);
|
|
2816
3028
|
}
|
|
2817
3029
|
console.log();
|
|
2818
3030
|
}
|
|
2819
|
-
console.log(
|
|
3031
|
+
console.log(chalk12.gray(`Total: ${AGENTS.length} agents supported`));
|
|
2820
3032
|
console.log();
|
|
2821
|
-
console.log(
|
|
2822
|
-
console.log(
|
|
2823
|
-
console.log(
|
|
2824
|
-
console.log(
|
|
3033
|
+
console.log(chalk12.gray("Commands:"));
|
|
3034
|
+
console.log(chalk12.gray(" lynxp agents enable <agent> Enable an agent"));
|
|
3035
|
+
console.log(chalk12.gray(" lynxp agents disable <agent> Disable an agent"));
|
|
3036
|
+
console.log(chalk12.gray(" lynxp agents detect Auto-detect agents"));
|
|
2825
3037
|
console.log();
|
|
2826
3038
|
}
|
|
2827
3039
|
async function enableAgent(agentId, options = {}) {
|
|
2828
3040
|
const cwd = process.cwd();
|
|
2829
3041
|
const configPath = join9(cwd, CONFIG_FILE2);
|
|
2830
3042
|
if (!existsSync6(configPath)) {
|
|
2831
|
-
console.log(
|
|
3043
|
+
console.log(chalk12.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
|
|
2832
3044
|
return;
|
|
2833
3045
|
}
|
|
2834
3046
|
let config2 = await loadConfig();
|
|
2835
3047
|
if (!config2) {
|
|
2836
|
-
console.log(
|
|
3048
|
+
console.log(chalk12.red("Could not load configuration."));
|
|
2837
3049
|
return;
|
|
2838
3050
|
}
|
|
2839
3051
|
if (!agentId || options.interactive) {
|
|
@@ -2843,7 +3055,7 @@ async function enableAgent(agentId, options = {}) {
|
|
|
2843
3055
|
value: agent2.id,
|
|
2844
3056
|
selected: enabledSet.has(agent2.id)
|
|
2845
3057
|
}));
|
|
2846
|
-
const { selected } = await
|
|
3058
|
+
const { selected } = await prompts6({
|
|
2847
3059
|
type: "multiselect",
|
|
2848
3060
|
name: "selected",
|
|
2849
3061
|
message: "Select agents to enable:",
|
|
@@ -2851,14 +3063,14 @@ async function enableAgent(agentId, options = {}) {
|
|
|
2851
3063
|
hint: "- Space to select, Enter to confirm"
|
|
2852
3064
|
});
|
|
2853
3065
|
if (!selected || selected.length === 0) {
|
|
2854
|
-
console.log(
|
|
3066
|
+
console.log(chalk12.yellow("No agents selected."));
|
|
2855
3067
|
return;
|
|
2856
3068
|
}
|
|
2857
3069
|
config2.exporters = selected;
|
|
2858
3070
|
await saveConfig(config2);
|
|
2859
|
-
console.log(
|
|
3071
|
+
console.log(chalk12.green(`\u2713 Enabled ${selected.length} agent${selected.length === 1 ? "" : "s"}`));
|
|
2860
3072
|
console.log();
|
|
2861
|
-
console.log(
|
|
3073
|
+
console.log(chalk12.gray("Run 'lynxp sync' to sync your rules."));
|
|
2862
3074
|
return;
|
|
2863
3075
|
}
|
|
2864
3076
|
const agent = getAgent(agentId);
|
|
@@ -2866,12 +3078,12 @@ async function enableAgent(agentId, options = {}) {
|
|
|
2866
3078
|
const similar = AGENTS.filter(
|
|
2867
3079
|
(a) => a.id.includes(agentId.toLowerCase()) || a.name.toLowerCase().includes(agentId.toLowerCase())
|
|
2868
3080
|
);
|
|
2869
|
-
console.log(
|
|
3081
|
+
console.log(chalk12.red(`Unknown agent: ${agentId}`));
|
|
2870
3082
|
if (similar.length > 0) {
|
|
2871
3083
|
console.log();
|
|
2872
|
-
console.log(
|
|
3084
|
+
console.log(chalk12.gray("Did you mean:"));
|
|
2873
3085
|
for (const a of similar.slice(0, 5)) {
|
|
2874
|
-
console.log(
|
|
3086
|
+
console.log(chalk12.gray(` ${a.id} - ${a.name}`));
|
|
2875
3087
|
}
|
|
2876
3088
|
}
|
|
2877
3089
|
return;
|
|
@@ -2880,48 +3092,48 @@ async function enableAgent(agentId, options = {}) {
|
|
|
2880
3092
|
config2.exporters = [];
|
|
2881
3093
|
}
|
|
2882
3094
|
if (config2.exporters.includes(agent.id)) {
|
|
2883
|
-
console.log(
|
|
3095
|
+
console.log(chalk12.yellow(`${agent.name} is already enabled.`));
|
|
2884
3096
|
return;
|
|
2885
3097
|
}
|
|
2886
3098
|
config2.exporters.push(agent.id);
|
|
2887
3099
|
await saveConfig(config2);
|
|
2888
|
-
console.log(
|
|
3100
|
+
console.log(chalk12.green(`\u2713 Enabled ${agent.name}`));
|
|
2889
3101
|
console.log();
|
|
2890
|
-
console.log(
|
|
2891
|
-
console.log(
|
|
3102
|
+
console.log(chalk12.gray(`Output: ${agent.output}`));
|
|
3103
|
+
console.log(chalk12.gray("Run 'lynxp sync' to sync your rules."));
|
|
2892
3104
|
}
|
|
2893
3105
|
async function disableAgent(agentId) {
|
|
2894
3106
|
if (!agentId) {
|
|
2895
|
-
console.log(
|
|
3107
|
+
console.log(chalk12.yellow("Usage: lynxp agents disable <agent>"));
|
|
2896
3108
|
return;
|
|
2897
3109
|
}
|
|
2898
3110
|
const cwd = process.cwd();
|
|
2899
3111
|
const configPath = join9(cwd, CONFIG_FILE2);
|
|
2900
3112
|
if (!existsSync6(configPath)) {
|
|
2901
|
-
console.log(
|
|
3113
|
+
console.log(chalk12.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
|
|
2902
3114
|
return;
|
|
2903
3115
|
}
|
|
2904
3116
|
const config2 = await loadConfig();
|
|
2905
3117
|
if (!config2) {
|
|
2906
|
-
console.log(
|
|
3118
|
+
console.log(chalk12.red("Could not load configuration."));
|
|
2907
3119
|
return;
|
|
2908
3120
|
}
|
|
2909
3121
|
if (!config2.exporters || !config2.exporters.includes(agentId)) {
|
|
2910
3122
|
const agent2 = getAgent(agentId);
|
|
2911
|
-
console.log(
|
|
3123
|
+
console.log(chalk12.yellow(`${agent2?.name ?? agentId} is not enabled.`));
|
|
2912
3124
|
return;
|
|
2913
3125
|
}
|
|
2914
3126
|
if (config2.exporters.length === 1) {
|
|
2915
|
-
console.log(
|
|
3127
|
+
console.log(chalk12.yellow("Cannot disable the last agent. At least one must be enabled."));
|
|
2916
3128
|
return;
|
|
2917
3129
|
}
|
|
2918
3130
|
config2.exporters = config2.exporters.filter((e) => e !== agentId);
|
|
2919
3131
|
await saveConfig(config2);
|
|
2920
3132
|
const agent = getAgent(agentId);
|
|
2921
|
-
console.log(
|
|
3133
|
+
console.log(chalk12.green(`\u2713 Disabled ${agent?.name ?? agentId}`));
|
|
2922
3134
|
}
|
|
2923
3135
|
async function detectAgentsInProject() {
|
|
2924
|
-
console.log(
|
|
3136
|
+
console.log(chalk12.cyan("\u{1F50D} Detecting AI agents..."));
|
|
2925
3137
|
console.log();
|
|
2926
3138
|
const detection = detectAgents();
|
|
2927
3139
|
console.log(formatDetectionResults(detection));
|
|
@@ -2933,10 +3145,10 @@ async function detectAgentsInProject() {
|
|
|
2933
3145
|
const enabledSet = new Set(config2?.exporters ?? []);
|
|
2934
3146
|
const newAgents = detection.detected.filter((d) => !enabledSet.has(d.agent.id));
|
|
2935
3147
|
if (newAgents.length === 0) {
|
|
2936
|
-
console.log(
|
|
3148
|
+
console.log(chalk12.gray("All detected agents are already enabled."));
|
|
2937
3149
|
return;
|
|
2938
3150
|
}
|
|
2939
|
-
const { enable } = await
|
|
3151
|
+
const { enable } = await prompts6({
|
|
2940
3152
|
type: "confirm",
|
|
2941
3153
|
name: "enable",
|
|
2942
3154
|
message: `Enable ${newAgents.length} detected agent${newAgents.length === 1 ? "" : "s"}?`,
|
|
@@ -2948,7 +3160,7 @@ async function detectAgentsInProject() {
|
|
|
2948
3160
|
...newAgents.map((d) => d.agent.id)
|
|
2949
3161
|
];
|
|
2950
3162
|
await saveConfig(config2);
|
|
2951
|
-
console.log(
|
|
3163
|
+
console.log(chalk12.green(`\u2713 Enabled ${newAgents.length} agent${newAgents.length === 1 ? "" : "s"}`));
|
|
2952
3164
|
}
|
|
2953
3165
|
}
|
|
2954
3166
|
async function loadConfig() {
|
|
@@ -2972,8 +3184,8 @@ async function saveConfig(config2) {
|
|
|
2972
3184
|
}
|
|
2973
3185
|
|
|
2974
3186
|
// src/commands/check.ts
|
|
2975
|
-
import
|
|
2976
|
-
import
|
|
3187
|
+
import chalk13 from "chalk";
|
|
3188
|
+
import ora10 from "ora";
|
|
2977
3189
|
import { readFile as readFile9, readdir as readdir5, stat as stat2 } from "fs/promises";
|
|
2978
3190
|
import { join as join10 } from "path";
|
|
2979
3191
|
import { existsSync as existsSync7 } from "fs";
|
|
@@ -3098,7 +3310,7 @@ async function checkCommand(options = {}) {
|
|
|
3098
3310
|
const cwd = process.cwd();
|
|
3099
3311
|
if (!isCi) {
|
|
3100
3312
|
console.log();
|
|
3101
|
-
console.log(
|
|
3313
|
+
console.log(chalk13.cyan("\u{1F431} LynxPrompt Check"));
|
|
3102
3314
|
console.log();
|
|
3103
3315
|
}
|
|
3104
3316
|
const result = {
|
|
@@ -3107,7 +3319,7 @@ async function checkCommand(options = {}) {
|
|
|
3107
3319
|
warnings: [],
|
|
3108
3320
|
files: []
|
|
3109
3321
|
};
|
|
3110
|
-
const spinner = !isCi ?
|
|
3322
|
+
const spinner = !isCi ? ora10("Scanning for configuration files...").start() : null;
|
|
3111
3323
|
for (const file of CONFIG_FILES2) {
|
|
3112
3324
|
const filePath = join10(cwd, file.path);
|
|
3113
3325
|
if (existsSync7(filePath)) {
|
|
@@ -3172,42 +3384,42 @@ async function checkCommand(options = {}) {
|
|
|
3172
3384
|
}
|
|
3173
3385
|
} else {
|
|
3174
3386
|
if (result.files.length === 0) {
|
|
3175
|
-
console.log(
|
|
3387
|
+
console.log(chalk13.yellow("\u26A0 No AI configuration files found."));
|
|
3176
3388
|
console.log();
|
|
3177
|
-
console.log(
|
|
3389
|
+
console.log(chalk13.gray("Run 'lynxp wizard' to create a configuration."));
|
|
3178
3390
|
return;
|
|
3179
3391
|
}
|
|
3180
|
-
console.log(
|
|
3392
|
+
console.log(chalk13.green(`\u2713 Found ${result.files.length} configuration file${result.files.length === 1 ? "" : "s"}:`));
|
|
3181
3393
|
for (const file of result.files) {
|
|
3182
|
-
console.log(
|
|
3394
|
+
console.log(chalk13.gray(` ${file}`));
|
|
3183
3395
|
}
|
|
3184
3396
|
console.log();
|
|
3185
3397
|
if (result.errors.length > 0) {
|
|
3186
|
-
console.log(
|
|
3398
|
+
console.log(chalk13.red(`\u2717 ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}:`));
|
|
3187
3399
|
for (const error of result.errors) {
|
|
3188
|
-
console.log(
|
|
3400
|
+
console.log(chalk13.red(` ${error}`));
|
|
3189
3401
|
}
|
|
3190
3402
|
console.log();
|
|
3191
3403
|
}
|
|
3192
3404
|
if (result.warnings.length > 0) {
|
|
3193
|
-
console.log(
|
|
3405
|
+
console.log(chalk13.yellow(`\u26A0 ${result.warnings.length} warning${result.warnings.length === 1 ? "" : "s"}:`));
|
|
3194
3406
|
for (const warning of result.warnings) {
|
|
3195
|
-
console.log(
|
|
3407
|
+
console.log(chalk13.yellow(` ${warning}`));
|
|
3196
3408
|
}
|
|
3197
3409
|
console.log();
|
|
3198
3410
|
}
|
|
3199
3411
|
if (result.valid) {
|
|
3200
|
-
console.log(
|
|
3412
|
+
console.log(chalk13.green("\u2705 Validation passed!"));
|
|
3201
3413
|
} else {
|
|
3202
|
-
console.log(
|
|
3414
|
+
console.log(chalk13.red("\u274C Validation failed. Fix the errors above."));
|
|
3203
3415
|
}
|
|
3204
3416
|
console.log();
|
|
3205
3417
|
}
|
|
3206
3418
|
}
|
|
3207
3419
|
|
|
3208
3420
|
// src/commands/diff.ts
|
|
3209
|
-
import
|
|
3210
|
-
import
|
|
3421
|
+
import chalk14 from "chalk";
|
|
3422
|
+
import ora11 from "ora";
|
|
3211
3423
|
import { readFile as readFile10 } from "fs/promises";
|
|
3212
3424
|
import { join as join11 } from "path";
|
|
3213
3425
|
import { existsSync as existsSync8 } from "fs";
|
|
@@ -3275,21 +3487,21 @@ function formatDiff(diff, contextLines = 3) {
|
|
|
3275
3487
|
let inHunk = false;
|
|
3276
3488
|
const changeIndices = diff.map((d, i) => d.type !== "same" ? i : -1).filter((i) => i !== -1);
|
|
3277
3489
|
if (changeIndices.length === 0) {
|
|
3278
|
-
return
|
|
3490
|
+
return chalk14.gray(" (no changes)");
|
|
3279
3491
|
}
|
|
3280
3492
|
for (let i = 0; i < diff.length; i++) {
|
|
3281
3493
|
const item = diff[i];
|
|
3282
3494
|
const nearChange = changeIndices.some((ci) => Math.abs(ci - i) <= contextLines);
|
|
3283
3495
|
if (nearChange) {
|
|
3284
3496
|
if (lastPrintedIndex !== -1 && i - lastPrintedIndex > 1) {
|
|
3285
|
-
output.push(
|
|
3497
|
+
output.push(chalk14.gray(" ..."));
|
|
3286
3498
|
}
|
|
3287
3499
|
if (item.type === "add") {
|
|
3288
|
-
output.push(
|
|
3500
|
+
output.push(chalk14.green(`+ ${item.line}`));
|
|
3289
3501
|
} else if (item.type === "remove") {
|
|
3290
|
-
output.push(
|
|
3502
|
+
output.push(chalk14.red(`- ${item.line}`));
|
|
3291
3503
|
} else {
|
|
3292
|
-
output.push(
|
|
3504
|
+
output.push(chalk14.gray(` ${item.line}`));
|
|
3293
3505
|
}
|
|
3294
3506
|
lastPrintedIndex = i;
|
|
3295
3507
|
}
|
|
@@ -3305,7 +3517,7 @@ function getDiffStats(diff) {
|
|
|
3305
3517
|
}
|
|
3306
3518
|
async function diffCommand(blueprintId, options = {}) {
|
|
3307
3519
|
console.log();
|
|
3308
|
-
console.log(
|
|
3520
|
+
console.log(chalk14.cyan("\u{1F431} LynxPrompt Diff"));
|
|
3309
3521
|
console.log();
|
|
3310
3522
|
const cwd = process.cwd();
|
|
3311
3523
|
if (options.local) {
|
|
@@ -3313,29 +3525,29 @@ async function diffCommand(blueprintId, options = {}) {
|
|
|
3313
3525
|
return;
|
|
3314
3526
|
}
|
|
3315
3527
|
if (!blueprintId) {
|
|
3316
|
-
console.log(
|
|
3528
|
+
console.log(chalk14.red("\u2717 Please provide a blueprint ID to compare with."));
|
|
3317
3529
|
console.log();
|
|
3318
|
-
console.log(
|
|
3319
|
-
console.log(
|
|
3320
|
-
console.log(
|
|
3530
|
+
console.log(chalk14.gray("Usage:"));
|
|
3531
|
+
console.log(chalk14.gray(" lynxp diff <blueprint-id> Compare local with remote blueprint"));
|
|
3532
|
+
console.log(chalk14.gray(" lynxp diff --local Compare .lynxprompt/rules/ with exports"));
|
|
3321
3533
|
return;
|
|
3322
3534
|
}
|
|
3323
3535
|
if (!isAuthenticated()) {
|
|
3324
|
-
console.log(
|
|
3325
|
-
console.log(
|
|
3536
|
+
console.log(chalk14.yellow("\u26A0 Not logged in. Some blueprints may not be accessible."));
|
|
3537
|
+
console.log(chalk14.gray("Run 'lynxp login' to authenticate."));
|
|
3326
3538
|
console.log();
|
|
3327
3539
|
}
|
|
3328
|
-
const spinner =
|
|
3540
|
+
const spinner = ora11("Fetching blueprint...").start();
|
|
3329
3541
|
try {
|
|
3330
3542
|
const { blueprint } = await api.getBlueprint(blueprintId);
|
|
3331
3543
|
spinner.stop();
|
|
3332
3544
|
if (!blueprint || !blueprint.content) {
|
|
3333
|
-
console.log(
|
|
3545
|
+
console.log(chalk14.red(`\u2717 Blueprint not found or has no content: ${blueprintId}`));
|
|
3334
3546
|
return;
|
|
3335
3547
|
}
|
|
3336
|
-
console.log(
|
|
3548
|
+
console.log(chalk14.green(`\u2713 Blueprint: ${blueprint.name || blueprintId}`));
|
|
3337
3549
|
if (blueprint.description) {
|
|
3338
|
-
console.log(
|
|
3550
|
+
console.log(chalk14.gray(` ${blueprint.description}`));
|
|
3339
3551
|
}
|
|
3340
3552
|
console.log();
|
|
3341
3553
|
const localPaths = [
|
|
@@ -3347,52 +3559,52 @@ async function diffCommand(blueprintId, options = {}) {
|
|
|
3347
3559
|
];
|
|
3348
3560
|
let localContent = null;
|
|
3349
3561
|
let localPath = null;
|
|
3350
|
-
for (const
|
|
3351
|
-
const fullPath = join11(cwd,
|
|
3562
|
+
for (const path2 of localPaths) {
|
|
3563
|
+
const fullPath = join11(cwd, path2);
|
|
3352
3564
|
if (existsSync8(fullPath)) {
|
|
3353
3565
|
try {
|
|
3354
3566
|
localContent = await readFile10(fullPath, "utf-8");
|
|
3355
|
-
localPath =
|
|
3567
|
+
localPath = path2;
|
|
3356
3568
|
break;
|
|
3357
3569
|
} catch {
|
|
3358
3570
|
}
|
|
3359
3571
|
}
|
|
3360
3572
|
}
|
|
3361
3573
|
if (!localContent) {
|
|
3362
|
-
console.log(
|
|
3363
|
-
console.log(
|
|
3574
|
+
console.log(chalk14.yellow("\u26A0 No local AI configuration file found."));
|
|
3575
|
+
console.log(chalk14.gray("Run 'lynxp wizard' to create one, or 'lynxp pull' to download the blueprint."));
|
|
3364
3576
|
return;
|
|
3365
3577
|
}
|
|
3366
|
-
console.log(
|
|
3578
|
+
console.log(chalk14.gray(`Comparing with: ${localPath}`));
|
|
3367
3579
|
console.log();
|
|
3368
3580
|
const diff = computeDiff(blueprint.content, localContent);
|
|
3369
3581
|
const stats = getDiffStats(diff);
|
|
3370
3582
|
if (stats.added === 0 && stats.removed === 0) {
|
|
3371
|
-
console.log(
|
|
3583
|
+
console.log(chalk14.green("\u2713 Files are identical!"));
|
|
3372
3584
|
} else {
|
|
3373
|
-
console.log(
|
|
3585
|
+
console.log(chalk14.gray("Changes (remote \u2192 local):"));
|
|
3374
3586
|
console.log();
|
|
3375
3587
|
console.log(formatDiff(diff));
|
|
3376
3588
|
console.log();
|
|
3377
|
-
console.log(
|
|
3589
|
+
console.log(chalk14.gray(`Summary: ${chalk14.green(`+${stats.added}`)} ${chalk14.red(`-${stats.removed}`)} lines changed`));
|
|
3378
3590
|
}
|
|
3379
3591
|
console.log();
|
|
3380
3592
|
} catch (error) {
|
|
3381
3593
|
spinner.stop();
|
|
3382
3594
|
if (error instanceof ApiRequestError) {
|
|
3383
3595
|
if (error.statusCode === 401) {
|
|
3384
|
-
console.log(
|
|
3596
|
+
console.log(chalk14.red("\u2717 Authentication required. Run 'lynxp login' first."));
|
|
3385
3597
|
} else if (error.statusCode === 404) {
|
|
3386
|
-
console.log(
|
|
3598
|
+
console.log(chalk14.red(`\u2717 Blueprint not found: ${blueprintId}`));
|
|
3387
3599
|
} else if (error.statusCode === 403) {
|
|
3388
|
-
console.log(
|
|
3600
|
+
console.log(chalk14.red("\u2717 Access denied to this blueprint."));
|
|
3389
3601
|
} else {
|
|
3390
|
-
console.log(
|
|
3602
|
+
console.log(chalk14.red(`\u2717 API error: ${error.message}`));
|
|
3391
3603
|
}
|
|
3392
3604
|
} else {
|
|
3393
|
-
console.log(
|
|
3605
|
+
console.log(chalk14.red("\u2717 Failed to fetch blueprint"));
|
|
3394
3606
|
if (error instanceof Error) {
|
|
3395
|
-
console.log(
|
|
3607
|
+
console.log(chalk14.gray(` ${error.message}`));
|
|
3396
3608
|
}
|
|
3397
3609
|
}
|
|
3398
3610
|
}
|
|
@@ -3400,22 +3612,22 @@ async function diffCommand(blueprintId, options = {}) {
|
|
|
3400
3612
|
async function diffLocal(cwd) {
|
|
3401
3613
|
const rulesDir = join11(cwd, ".lynxprompt/rules");
|
|
3402
3614
|
if (!existsSync8(rulesDir)) {
|
|
3403
|
-
console.log(
|
|
3404
|
-
console.log(
|
|
3615
|
+
console.log(chalk14.yellow("\u26A0 No .lynxprompt/rules/ directory found."));
|
|
3616
|
+
console.log(chalk14.gray("Run 'lynxp init' to set up the advanced workflow, or 'lynxp wizard' for simple file generation."));
|
|
3405
3617
|
return;
|
|
3406
3618
|
}
|
|
3407
|
-
console.log(
|
|
3619
|
+
console.log(chalk14.gray("Comparing .lynxprompt/rules/ with exported files..."));
|
|
3408
3620
|
console.log();
|
|
3409
3621
|
const rulesPath = join11(rulesDir, "agents.md");
|
|
3410
3622
|
if (!existsSync8(rulesPath)) {
|
|
3411
|
-
console.log(
|
|
3623
|
+
console.log(chalk14.yellow("\u26A0 No rules files found in .lynxprompt/rules/"));
|
|
3412
3624
|
return;
|
|
3413
3625
|
}
|
|
3414
3626
|
let rulesContent;
|
|
3415
3627
|
try {
|
|
3416
3628
|
rulesContent = await readFile10(rulesPath, "utf-8");
|
|
3417
3629
|
} catch {
|
|
3418
|
-
console.log(
|
|
3630
|
+
console.log(chalk14.red("\u2717 Could not read .lynxprompt/rules/agents.md"));
|
|
3419
3631
|
return;
|
|
3420
3632
|
}
|
|
3421
3633
|
const exportedFiles = [
|
|
@@ -3440,12 +3652,12 @@ async function diffLocal(cwd) {
|
|
|
3440
3652
|
const stats = getDiffStats(diff);
|
|
3441
3653
|
if (stats.added > 0 || stats.removed > 0) {
|
|
3442
3654
|
hasChanges = true;
|
|
3443
|
-
console.log(
|
|
3655
|
+
console.log(chalk14.yellow(`\u26A0 ${file.name} differs from source:`));
|
|
3444
3656
|
console.log(formatDiff(diff));
|
|
3445
|
-
console.log(
|
|
3657
|
+
console.log(chalk14.gray(` ${chalk14.green(`+${stats.added}`)} ${chalk14.red(`-${stats.removed}`)} lines`));
|
|
3446
3658
|
console.log();
|
|
3447
3659
|
} else {
|
|
3448
|
-
console.log(
|
|
3660
|
+
console.log(chalk14.green(`\u2713 ${file.name} is in sync`));
|
|
3449
3661
|
}
|
|
3450
3662
|
} catch {
|
|
3451
3663
|
}
|
|
@@ -3453,18 +3665,18 @@ async function diffLocal(cwd) {
|
|
|
3453
3665
|
}
|
|
3454
3666
|
if (!hasChanges) {
|
|
3455
3667
|
console.log();
|
|
3456
|
-
console.log(
|
|
3668
|
+
console.log(chalk14.green("\u2713 All exported files are in sync with .lynxprompt/rules/"));
|
|
3457
3669
|
} else {
|
|
3458
3670
|
console.log();
|
|
3459
|
-
console.log(
|
|
3671
|
+
console.log(chalk14.gray("Run 'lynxp sync' to update exported files from .lynxprompt/rules/"));
|
|
3460
3672
|
}
|
|
3461
3673
|
console.log();
|
|
3462
3674
|
}
|
|
3463
3675
|
|
|
3464
3676
|
// src/commands/link.ts
|
|
3465
|
-
import
|
|
3466
|
-
import
|
|
3467
|
-
import
|
|
3677
|
+
import chalk15 from "chalk";
|
|
3678
|
+
import ora12 from "ora";
|
|
3679
|
+
import prompts7 from "prompts";
|
|
3468
3680
|
import { join as join12 } from "path";
|
|
3469
3681
|
import { existsSync as existsSync9 } from "fs";
|
|
3470
3682
|
function getSourceFromVisibility2(visibility) {
|
|
@@ -3486,194 +3698,195 @@ async function linkCommand(file, blueprintId, options = {}) {
|
|
|
3486
3698
|
return;
|
|
3487
3699
|
}
|
|
3488
3700
|
if (!file) {
|
|
3489
|
-
console.log(
|
|
3701
|
+
console.log(chalk15.red("\u2717 Please provide a file path to link."));
|
|
3490
3702
|
console.log();
|
|
3491
|
-
console.log(
|
|
3492
|
-
console.log(
|
|
3493
|
-
console.log(
|
|
3703
|
+
console.log(chalk15.gray("Usage:"));
|
|
3704
|
+
console.log(chalk15.gray(" lynxp link <file> <blueprint-id> Link a local file to a cloud blueprint"));
|
|
3705
|
+
console.log(chalk15.gray(" lynxp link --list List all tracked blueprints"));
|
|
3494
3706
|
console.log();
|
|
3495
|
-
console.log(
|
|
3496
|
-
console.log(
|
|
3707
|
+
console.log(chalk15.gray("Example:"));
|
|
3708
|
+
console.log(chalk15.gray(" lynxp link AGENTS.md bp_abc123"));
|
|
3497
3709
|
return;
|
|
3498
3710
|
}
|
|
3499
3711
|
if (!blueprintId) {
|
|
3500
|
-
console.log(
|
|
3712
|
+
console.log(chalk15.red("\u2717 Please provide a blueprint ID to link to."));
|
|
3501
3713
|
console.log();
|
|
3502
|
-
console.log(
|
|
3503
|
-
console.log(
|
|
3714
|
+
console.log(chalk15.gray("Usage: lynxp link <file> <blueprint-id>"));
|
|
3715
|
+
console.log(chalk15.gray("Example: lynxp link AGENTS.md bp_abc123"));
|
|
3504
3716
|
console.log();
|
|
3505
|
-
console.log(
|
|
3506
|
-
console.log(
|
|
3507
|
-
console.log(
|
|
3717
|
+
console.log(chalk15.gray("To find blueprint IDs:"));
|
|
3718
|
+
console.log(chalk15.gray(" lynxp list - Show your blueprints"));
|
|
3719
|
+
console.log(chalk15.gray(" lynxp search <query> - Search marketplace"));
|
|
3508
3720
|
return;
|
|
3509
3721
|
}
|
|
3510
3722
|
const filePath = join12(cwd, file);
|
|
3511
3723
|
if (!existsSync9(filePath)) {
|
|
3512
|
-
console.log(
|
|
3724
|
+
console.log(chalk15.red(`\u2717 File not found: ${file}`));
|
|
3513
3725
|
return;
|
|
3514
3726
|
}
|
|
3515
3727
|
const existing = await findBlueprintByFile(cwd, file);
|
|
3516
3728
|
if (existing) {
|
|
3517
|
-
console.log(
|
|
3518
|
-
const { proceed } = await
|
|
3729
|
+
console.log(chalk15.yellow(`\u26A0 This file is already linked to: ${existing.id}`));
|
|
3730
|
+
const { proceed } = await prompts7({
|
|
3519
3731
|
type: "confirm",
|
|
3520
3732
|
name: "proceed",
|
|
3521
3733
|
message: "Replace the existing link?",
|
|
3522
3734
|
initial: false
|
|
3523
3735
|
});
|
|
3524
3736
|
if (!proceed) {
|
|
3525
|
-
console.log(
|
|
3737
|
+
console.log(chalk15.gray("Cancelled."));
|
|
3526
3738
|
return;
|
|
3527
3739
|
}
|
|
3528
3740
|
}
|
|
3529
3741
|
if (!isAuthenticated()) {
|
|
3530
3742
|
console.log(
|
|
3531
|
-
|
|
3743
|
+
chalk15.yellow("Not logged in. Run 'lynxp login' to authenticate.")
|
|
3532
3744
|
);
|
|
3533
3745
|
process.exit(1);
|
|
3534
3746
|
}
|
|
3535
|
-
const spinner =
|
|
3747
|
+
const spinner = ora12(`Fetching blueprint ${chalk15.cyan(blueprintId)}...`).start();
|
|
3536
3748
|
try {
|
|
3537
3749
|
const { blueprint } = await api.getBlueprint(blueprintId);
|
|
3538
3750
|
spinner.stop();
|
|
3539
3751
|
const source = getSourceFromVisibility2(blueprint.visibility);
|
|
3540
3752
|
const isMarketplace = source === "marketplace";
|
|
3541
3753
|
console.log();
|
|
3542
|
-
console.log(
|
|
3754
|
+
console.log(chalk15.cyan(`\u{1F431} Blueprint: ${chalk15.bold(blueprint.name)}`));
|
|
3543
3755
|
if (blueprint.description) {
|
|
3544
|
-
console.log(
|
|
3756
|
+
console.log(chalk15.gray(` ${blueprint.description}`));
|
|
3545
3757
|
}
|
|
3546
|
-
console.log(
|
|
3758
|
+
console.log(chalk15.gray(` Visibility: ${blueprint.visibility}`));
|
|
3547
3759
|
console.log();
|
|
3548
3760
|
if (isMarketplace) {
|
|
3549
|
-
console.log(
|
|
3550
|
-
console.log(
|
|
3551
|
-
console.log(
|
|
3761
|
+
console.log(chalk15.yellow("\u26A0 This is a marketplace blueprint."));
|
|
3762
|
+
console.log(chalk15.gray(" Your local changes will NOT sync back to the cloud."));
|
|
3763
|
+
console.log(chalk15.gray(" To make changes, you'll need to create your own copy."));
|
|
3552
3764
|
console.log();
|
|
3553
3765
|
}
|
|
3554
|
-
const { confirm } = await
|
|
3766
|
+
const { confirm } = await prompts7({
|
|
3555
3767
|
type: "confirm",
|
|
3556
3768
|
name: "confirm",
|
|
3557
|
-
message: `Link ${
|
|
3769
|
+
message: `Link ${chalk15.cyan(file)} to ${chalk15.cyan(blueprint.name)}?`,
|
|
3558
3770
|
initial: true
|
|
3559
3771
|
});
|
|
3560
3772
|
if (!confirm) {
|
|
3561
|
-
console.log(
|
|
3773
|
+
console.log(chalk15.gray("Cancelled."));
|
|
3562
3774
|
return;
|
|
3563
3775
|
}
|
|
3564
3776
|
await linkBlueprint(cwd, file, blueprint.id, blueprint.name, source);
|
|
3565
3777
|
console.log();
|
|
3566
|
-
console.log(
|
|
3778
|
+
console.log(chalk15.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
|
|
3567
3779
|
console.log();
|
|
3568
|
-
console.log(
|
|
3569
|
-
console.log(
|
|
3570
|
-
console.log(
|
|
3571
|
-
console.log(
|
|
3780
|
+
console.log(chalk15.gray("Next steps:"));
|
|
3781
|
+
console.log(chalk15.gray(` \u2022 Run 'lynxp pull ${blueprintId}' to update local file from cloud`));
|
|
3782
|
+
console.log(chalk15.gray(` \u2022 Run 'lynxp diff ${blueprintId}' to see differences`));
|
|
3783
|
+
console.log(chalk15.gray(` \u2022 Run 'lynxp status' to see all tracked blueprints`));
|
|
3572
3784
|
if (!isMarketplace) {
|
|
3573
|
-
console.log(
|
|
3785
|
+
console.log(chalk15.gray(` \u2022 Run 'lynxp push ${file}' to push local changes to cloud`));
|
|
3574
3786
|
}
|
|
3575
3787
|
console.log();
|
|
3576
3788
|
} catch (error) {
|
|
3577
3789
|
spinner.stop();
|
|
3578
3790
|
if (error instanceof ApiRequestError) {
|
|
3579
3791
|
if (error.statusCode === 404) {
|
|
3580
|
-
console.log(
|
|
3581
|
-
console.log(
|
|
3792
|
+
console.log(chalk15.red(`\u2717 Blueprint not found: ${blueprintId}`));
|
|
3793
|
+
console.log(chalk15.gray(" Make sure the ID is correct. Use 'lynxp list' or 'lynxp search' to find blueprints."));
|
|
3582
3794
|
} else if (error.statusCode === 403) {
|
|
3583
|
-
console.log(
|
|
3795
|
+
console.log(chalk15.red("\u2717 You don't have access to this blueprint."));
|
|
3584
3796
|
} else {
|
|
3585
|
-
console.log(
|
|
3797
|
+
console.log(chalk15.red(`\u2717 Error: ${error.message}`));
|
|
3586
3798
|
}
|
|
3587
3799
|
} else {
|
|
3588
|
-
console.log(
|
|
3800
|
+
console.log(chalk15.red("\u2717 An unexpected error occurred."));
|
|
3589
3801
|
}
|
|
3590
3802
|
}
|
|
3591
3803
|
}
|
|
3592
3804
|
async function unlinkCommand(file) {
|
|
3593
3805
|
const cwd = process.cwd();
|
|
3594
3806
|
if (!file) {
|
|
3595
|
-
console.log(
|
|
3807
|
+
console.log(chalk15.red("\u2717 Please provide a file path to unlink."));
|
|
3596
3808
|
console.log();
|
|
3597
|
-
console.log(
|
|
3598
|
-
console.log(
|
|
3809
|
+
console.log(chalk15.gray("Usage: lynxp unlink <file>"));
|
|
3810
|
+
console.log(chalk15.gray("Example: lynxp unlink AGENTS.md"));
|
|
3599
3811
|
return;
|
|
3600
3812
|
}
|
|
3601
3813
|
const tracked = await findBlueprintByFile(cwd, file);
|
|
3602
3814
|
if (!tracked) {
|
|
3603
|
-
console.log(
|
|
3815
|
+
console.log(chalk15.yellow(`\u26A0 File is not linked to any blueprint: ${file}`));
|
|
3604
3816
|
return;
|
|
3605
3817
|
}
|
|
3606
3818
|
console.log();
|
|
3607
|
-
console.log(
|
|
3608
|
-
console.log(
|
|
3609
|
-
console.log(
|
|
3819
|
+
console.log(chalk15.cyan(`Currently linked to: ${tracked.id}`));
|
|
3820
|
+
console.log(chalk15.gray(` Name: ${tracked.name}`));
|
|
3821
|
+
console.log(chalk15.gray(` Source: ${tracked.source}`));
|
|
3610
3822
|
console.log();
|
|
3611
|
-
const { confirm } = await
|
|
3823
|
+
const { confirm } = await prompts7({
|
|
3612
3824
|
type: "confirm",
|
|
3613
3825
|
name: "confirm",
|
|
3614
|
-
message: `Unlink ${
|
|
3826
|
+
message: `Unlink ${chalk15.cyan(file)} from ${chalk15.cyan(tracked.name)}?`,
|
|
3615
3827
|
initial: true
|
|
3616
3828
|
});
|
|
3617
3829
|
if (!confirm) {
|
|
3618
|
-
console.log(
|
|
3830
|
+
console.log(chalk15.gray("Cancelled."));
|
|
3619
3831
|
return;
|
|
3620
3832
|
}
|
|
3621
3833
|
const success = await untrackBlueprint(cwd, file);
|
|
3622
3834
|
if (success) {
|
|
3623
3835
|
console.log();
|
|
3624
|
-
console.log(
|
|
3625
|
-
console.log(
|
|
3626
|
-
console.log(
|
|
3836
|
+
console.log(chalk15.green(`\u2705 Unlinked: ${file}`));
|
|
3837
|
+
console.log(chalk15.gray(" The file is now a standalone local file."));
|
|
3838
|
+
console.log(chalk15.gray(" It will no longer receive updates from the cloud blueprint."));
|
|
3627
3839
|
console.log();
|
|
3628
3840
|
} else {
|
|
3629
|
-
console.log(
|
|
3841
|
+
console.log(chalk15.red("\u2717 Failed to unlink file."));
|
|
3630
3842
|
}
|
|
3631
3843
|
}
|
|
3632
3844
|
async function listTrackedBlueprints(cwd) {
|
|
3633
3845
|
console.log();
|
|
3634
|
-
console.log(
|
|
3846
|
+
console.log(chalk15.cyan("\u{1F431} Tracked Blueprints"));
|
|
3635
3847
|
console.log();
|
|
3636
3848
|
const status = await checkSyncStatus(cwd);
|
|
3637
3849
|
if (status.length === 0) {
|
|
3638
|
-
console.log(
|
|
3850
|
+
console.log(chalk15.gray("No blueprints are currently tracked."));
|
|
3639
3851
|
console.log();
|
|
3640
|
-
console.log(
|
|
3641
|
-
console.log(
|
|
3642
|
-
console.log(
|
|
3852
|
+
console.log(chalk15.gray("To track a blueprint:"));
|
|
3853
|
+
console.log(chalk15.gray(" lynxp pull <blueprint-id> Download and track a blueprint"));
|
|
3854
|
+
console.log(chalk15.gray(" lynxp link <file> <id> Link an existing file to a blueprint"));
|
|
3643
3855
|
return;
|
|
3644
3856
|
}
|
|
3645
3857
|
for (const { blueprint, localModified, fileExists: fileExists2 } of status) {
|
|
3646
|
-
const statusIcon = !fileExists2 ?
|
|
3858
|
+
const statusIcon = !fileExists2 ? chalk15.red("\u2717") : localModified ? chalk15.yellow("\u25CF") : chalk15.green("\u2713");
|
|
3647
3859
|
const sourceLabel = {
|
|
3648
|
-
marketplace:
|
|
3649
|
-
team:
|
|
3650
|
-
private:
|
|
3651
|
-
local:
|
|
3860
|
+
marketplace: chalk15.gray("[marketplace]"),
|
|
3861
|
+
team: chalk15.blue("[team]"),
|
|
3862
|
+
private: chalk15.green("[private]"),
|
|
3863
|
+
local: chalk15.gray("[local]")
|
|
3652
3864
|
}[blueprint.source];
|
|
3653
|
-
console.log(`${statusIcon} ${
|
|
3865
|
+
console.log(`${statusIcon} ${chalk15.cyan(blueprint.file)}`);
|
|
3654
3866
|
console.log(` ${sourceLabel} ${blueprint.name}`);
|
|
3655
|
-
console.log(` ${
|
|
3867
|
+
console.log(` ${chalk15.gray(`ID: ${blueprint.id}`)}`);
|
|
3656
3868
|
if (!fileExists2) {
|
|
3657
|
-
console.log(
|
|
3869
|
+
console.log(chalk15.red(` \u26A0 File not found`));
|
|
3658
3870
|
} else if (localModified) {
|
|
3659
|
-
console.log(
|
|
3871
|
+
console.log(chalk15.yellow(` \u26A0 Local changes detected`));
|
|
3660
3872
|
}
|
|
3661
3873
|
console.log();
|
|
3662
3874
|
}
|
|
3663
|
-
console.log(
|
|
3664
|
-
console.log(
|
|
3875
|
+
console.log(chalk15.gray("Legend:"));
|
|
3876
|
+
console.log(chalk15.gray(` ${chalk15.green("\u2713")} In sync ${chalk15.yellow("\u25CF")} Modified locally ${chalk15.red("\u2717")} Missing`));
|
|
3665
3877
|
console.log();
|
|
3666
3878
|
}
|
|
3667
3879
|
|
|
3668
3880
|
// src/index.ts
|
|
3669
3881
|
var program = new Command();
|
|
3670
|
-
program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version("0.
|
|
3882
|
+
program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version("0.3.0");
|
|
3671
3883
|
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
3884
|
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
3885
|
program.command("status").description("Show current AI configuration and tracked blueprints").action(statusCommand);
|
|
3674
3886
|
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
3887
|
program.command("search <query>").description("Search public blueprints in the marketplace").option("-l, --limit <number>", "Number of results", "20").action(searchCommand);
|
|
3676
3888
|
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);
|
|
3889
|
+
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
3890
|
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
3891
|
program.command("unlink <file>").description("Disconnect a local file from its cloud blueprint").action(unlinkCommand);
|
|
3679
3892
|
program.command("diff [blueprint-id]").description("Show changes between local and remote blueprint").option("--local", "Compare .lynxprompt/rules/ with exported files").action(diffCommand);
|
|
@@ -3686,32 +3899,33 @@ program.command("whoami").description("Show current authenticated user").action(
|
|
|
3686
3899
|
program.addHelpText(
|
|
3687
3900
|
"beforeAll",
|
|
3688
3901
|
`
|
|
3689
|
-
${
|
|
3690
|
-
${
|
|
3902
|
+
${chalk16.cyan("\u{1F431} LynxPrompt CLI")} ${chalk16.gray("(also available as: lynxp)")}
|
|
3903
|
+
${chalk16.gray("Generate AI IDE configuration files from your terminal")}
|
|
3691
3904
|
`
|
|
3692
3905
|
);
|
|
3693
3906
|
program.addHelpText(
|
|
3694
3907
|
"after",
|
|
3695
3908
|
`
|
|
3696
|
-
${
|
|
3697
|
-
${
|
|
3698
|
-
${
|
|
3699
|
-
${
|
|
3700
|
-
|
|
3701
|
-
${
|
|
3702
|
-
${
|
|
3703
|
-
${
|
|
3704
|
-
${
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
${
|
|
3709
|
-
${
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3909
|
+
${chalk16.cyan("Quick Start:")}
|
|
3910
|
+
${chalk16.white("$ lynxp wizard")} ${chalk16.gray("Generate config interactively")}
|
|
3911
|
+
${chalk16.white("$ lynxp wizard -y")} ${chalk16.gray("Generate AGENTS.md with defaults")}
|
|
3912
|
+
${chalk16.white("$ lynxp wizard -f cursor")} ${chalk16.gray("Generate .cursor/rules/")}
|
|
3913
|
+
|
|
3914
|
+
${chalk16.cyan("Marketplace:")}
|
|
3915
|
+
${chalk16.white("$ lynxp search nextjs")} ${chalk16.gray("Search blueprints")}
|
|
3916
|
+
${chalk16.white("$ lynxp pull bp_abc123")} ${chalk16.gray("Download and track a blueprint")}
|
|
3917
|
+
${chalk16.white("$ lynxp push")} ${chalk16.gray("Push local file to cloud")}
|
|
3918
|
+
${chalk16.white("$ lynxp link --list")} ${chalk16.gray("Show tracked blueprints")}
|
|
3919
|
+
|
|
3920
|
+
${chalk16.cyan("Blueprint Tracking:")}
|
|
3921
|
+
${chalk16.white("$ lynxp link AGENTS.md bp_xyz")} ${chalk16.gray("Link existing file to blueprint")}
|
|
3922
|
+
${chalk16.white("$ lynxp unlink AGENTS.md")} ${chalk16.gray("Disconnect from cloud")}
|
|
3923
|
+
${chalk16.white("$ lynxp diff bp_abc123")} ${chalk16.gray("Show changes vs cloud version")}
|
|
3924
|
+
|
|
3925
|
+
${chalk16.cyan("CI/CD:")}
|
|
3926
|
+
${chalk16.white("$ lynxp check --ci")} ${chalk16.gray("Validate config (exit code)")}
|
|
3927
|
+
|
|
3928
|
+
${chalk16.gray("Docs: https://lynxprompt.com/docs/cli")}
|
|
3715
3929
|
`
|
|
3716
3930
|
);
|
|
3717
3931
|
program.parse();
|