lynxprompt 2.1.39 → 2.1.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/index.js +628 -444
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk21 from "chalk";
|
|
6
6
|
|
|
7
7
|
// src/commands/login.ts
|
|
8
8
|
import chalk from "chalk";
|
|
@@ -32,8 +32,7 @@ var config = new Conf({
|
|
|
32
32
|
properties: {
|
|
33
33
|
id: { type: "string" },
|
|
34
34
|
email: { type: "string" },
|
|
35
|
-
name: { type: ["string", "null"] }
|
|
36
|
-
plan: { type: "string" }
|
|
35
|
+
name: { type: ["string", "null"] }
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
},
|
|
@@ -546,8 +545,7 @@ async function whoamiCommand() {
|
|
|
546
545
|
setUser({
|
|
547
546
|
id: user.id,
|
|
548
547
|
email: user.email,
|
|
549
|
-
name: user.name
|
|
550
|
-
plan: user.plan
|
|
548
|
+
name: user.name
|
|
551
549
|
});
|
|
552
550
|
console.log();
|
|
553
551
|
console.log(chalk3.cyan("\u{1F431} LynxPrompt Account"));
|
|
@@ -735,7 +733,11 @@ async function trackBlueprint(cwd, blueprint) {
|
|
|
735
733
|
canPull,
|
|
736
734
|
hierarchyId: blueprint.hierarchyId,
|
|
737
735
|
hierarchyName: blueprint.hierarchyName,
|
|
738
|
-
repositoryPath: blueprint.repositoryPath
|
|
736
|
+
repositoryPath: blueprint.repositoryPath,
|
|
737
|
+
...blueprint.variables ? {
|
|
738
|
+
variables: blueprint.variables,
|
|
739
|
+
localChecksum: calculateChecksum(blueprint.localContent ?? blueprint.content)
|
|
740
|
+
} : {}
|
|
739
741
|
});
|
|
740
742
|
await saveBlueprints(cwd, config2);
|
|
741
743
|
}
|
|
@@ -748,7 +750,7 @@ async function hasLocalChanges(cwd, tracked) {
|
|
|
748
750
|
const filePath = join(cwd, tracked.file);
|
|
749
751
|
const content = await readFile(filePath, "utf-8");
|
|
750
752
|
const currentChecksum = calculateChecksum(content);
|
|
751
|
-
return currentChecksum !== tracked.checksum;
|
|
753
|
+
return currentChecksum !== (tracked.localChecksum ?? tracked.checksum);
|
|
752
754
|
} catch {
|
|
753
755
|
return false;
|
|
754
756
|
}
|
|
@@ -758,10 +760,15 @@ async function updateChecksum(cwd, file, content) {
|
|
|
758
760
|
const blueprint = config2.blueprints.find((b) => sameFile(cwd, b.file, file));
|
|
759
761
|
if (blueprint) {
|
|
760
762
|
blueprint.checksum = calculateChecksum(content);
|
|
763
|
+
delete blueprint.variables;
|
|
764
|
+
delete blueprint.localChecksum;
|
|
761
765
|
blueprint.pulledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
762
766
|
await saveBlueprints(cwd, config2);
|
|
763
767
|
}
|
|
764
768
|
}
|
|
769
|
+
function conflictFilePath(cwd, file) {
|
|
770
|
+
return join(cwd, `${file}.remote`);
|
|
771
|
+
}
|
|
765
772
|
async function untrackBlueprint(cwd, file) {
|
|
766
773
|
const config2 = await loadBlueprints(cwd);
|
|
767
774
|
const initialCount = config2.blueprints.length;
|
|
@@ -2981,7 +2988,35 @@ function matchConfigFile(relativePath) {
|
|
|
2981
2988
|
return null;
|
|
2982
2989
|
}
|
|
2983
2990
|
|
|
2991
|
+
// src/utils/variables.ts
|
|
2992
|
+
var VARIABLE_PATTERN_SOURCE = String.raw`\[\[([A-Za-z_][A-Za-z0-9_]*)(?:\|((?:(?!\[\[)[\s\S])*?)\]\](?!\])|\]\])`;
|
|
2993
|
+
function fillBlueprintVariables(content, values) {
|
|
2994
|
+
const missing = /* @__PURE__ */ new Set();
|
|
2995
|
+
const filled = content.replace(
|
|
2996
|
+
new RegExp(VARIABLE_PATTERN_SOURCE, "g"),
|
|
2997
|
+
(match, name, fallback) => {
|
|
2998
|
+
const key = name.toUpperCase();
|
|
2999
|
+
if (Object.prototype.hasOwnProperty.call(values, key)) return values[key];
|
|
3000
|
+
if (fallback !== void 0) return fallback;
|
|
3001
|
+
missing.add(key);
|
|
3002
|
+
return match;
|
|
3003
|
+
}
|
|
3004
|
+
);
|
|
3005
|
+
return { content: filled, missing: [...missing] };
|
|
3006
|
+
}
|
|
3007
|
+
|
|
2984
3008
|
// src/commands/pull.ts
|
|
3009
|
+
function contentToWrite(content, options, label) {
|
|
3010
|
+
if (options.vars === void 0) return content;
|
|
3011
|
+
const { content: filled, missing } = fillBlueprintVariables(content, parseVariablesString(options.vars));
|
|
3012
|
+
if (missing.length > 0) {
|
|
3013
|
+
console.log(chalk5.yellow(` \u26A0 ${label}: no value for ${missing.join(", ")} (left as [[...]])`));
|
|
3014
|
+
}
|
|
3015
|
+
return filled;
|
|
3016
|
+
}
|
|
3017
|
+
function pulledVariables(options, written, content) {
|
|
3018
|
+
return options.vars === void 0 || written === content ? void 0 : parseVariablesString(options.vars);
|
|
3019
|
+
}
|
|
2985
3020
|
function getSourceFromVisibility(visibility) {
|
|
2986
3021
|
switch (visibility) {
|
|
2987
3022
|
case "PUBLIC":
|
|
@@ -3087,7 +3122,8 @@ async function pullHierarchy(id, options) {
|
|
|
3087
3122
|
if (dir !== "." && dir !== options.output) {
|
|
3088
3123
|
await mkdir2(dir, { recursive: true });
|
|
3089
3124
|
}
|
|
3090
|
-
|
|
3125
|
+
const written = contentToWrite(blueprint.content, options, filename);
|
|
3126
|
+
await writeFile2(outputPath, written, "utf-8");
|
|
3091
3127
|
if (options.track !== false) {
|
|
3092
3128
|
const source = getSourceFromVisibility(blueprint.visibility);
|
|
3093
3129
|
await trackBlueprint(cwd, {
|
|
@@ -3099,7 +3135,9 @@ async function pullHierarchy(id, options) {
|
|
|
3099
3135
|
source,
|
|
3100
3136
|
hierarchyId: id,
|
|
3101
3137
|
hierarchyName: hierarchy.name,
|
|
3102
|
-
repositoryPath: bp.repository_path || void 0
|
|
3138
|
+
repositoryPath: bp.repository_path || void 0,
|
|
3139
|
+
localContent: written,
|
|
3140
|
+
variables: pulledVariables(options, written, blueprint.content)
|
|
3103
3141
|
});
|
|
3104
3142
|
}
|
|
3105
3143
|
console.log(chalk5.green(` \u2713 ${filename}`));
|
|
@@ -3268,7 +3306,8 @@ async function pullBlueprint(id, options) {
|
|
|
3268
3306
|
if (dir !== ".") {
|
|
3269
3307
|
await mkdir2(dir, { recursive: true });
|
|
3270
3308
|
}
|
|
3271
|
-
|
|
3309
|
+
const written = contentToWrite(blueprint.content, options, filename);
|
|
3310
|
+
await writeFile2(outputPath, written, "utf-8");
|
|
3272
3311
|
if (options.track !== false) {
|
|
3273
3312
|
await trackBlueprint(cwd, {
|
|
3274
3313
|
id: blueprint.id,
|
|
@@ -3277,7 +3316,9 @@ async function pullBlueprint(id, options) {
|
|
|
3277
3316
|
content: blueprint.content,
|
|
3278
3317
|
source,
|
|
3279
3318
|
hierarchyId: blueprint.hierarchy_id || void 0,
|
|
3280
|
-
repositoryPath: blueprint.repository_path || void 0
|
|
3319
|
+
repositoryPath: blueprint.repository_path || void 0,
|
|
3320
|
+
localContent: written,
|
|
3321
|
+
variables: pulledVariables(options, written, blueprint.content)
|
|
3281
3322
|
});
|
|
3282
3323
|
}
|
|
3283
3324
|
console.log(chalk5.green(`\u2705 Downloaded: ${chalk5.bold(outputPath)}`));
|
|
@@ -3286,8 +3327,8 @@ async function pullBlueprint(id, options) {
|
|
|
3286
3327
|
if (blueprint.content_checksum) {
|
|
3287
3328
|
console.log(chalk5.gray(` Checksum: ${blueprint.content_checksum}`));
|
|
3288
3329
|
}
|
|
3289
|
-
if (isMarketplace) {
|
|
3290
|
-
console.log(chalk5.gray(` Updates: Run 'lynxp
|
|
3330
|
+
if (isMarketplace || options.vars !== void 0) {
|
|
3331
|
+
console.log(chalk5.gray(` Updates: Run 'lynxp sync ${trackedFile}' to get cloud updates`));
|
|
3291
3332
|
} else {
|
|
3292
3333
|
console.log(chalk5.gray(` Sync: Run 'lynxp push ${trackedFile}' to push changes`));
|
|
3293
3334
|
}
|
|
@@ -3629,11 +3670,26 @@ async function pushCommand(fileArg, options) {
|
|
|
3629
3670
|
const filename = path2.basename(file);
|
|
3630
3671
|
const linked = await findBlueprintByFile(cwd, file);
|
|
3631
3672
|
if (linked) {
|
|
3673
|
+
const refusal = refusePush(cwd, linked, options);
|
|
3674
|
+
if (refusal) {
|
|
3675
|
+
console.log(chalk6.yellow(`\u26A0 Not pushed: ${refusal}`));
|
|
3676
|
+
process.exit(1);
|
|
3677
|
+
}
|
|
3632
3678
|
await updateBlueprint(cwd, file, linked.id, content, options, linked.checksum);
|
|
3633
3679
|
} else {
|
|
3634
3680
|
await createOrLinkBlueprint(cwd, file, filename, content, options, fileArg !== void 0);
|
|
3635
3681
|
}
|
|
3636
3682
|
}
|
|
3683
|
+
function refusePush(cwd, tracked, options) {
|
|
3684
|
+
if (options.force) return null;
|
|
3685
|
+
if (fs.existsSync(conflictFilePath(cwd, tracked.file))) {
|
|
3686
|
+
return `${tracked.file} has an unresolved sync conflict. Merge ${tracked.file}.remote into it and delete ${tracked.file}.remote first, or use --force.`;
|
|
3687
|
+
}
|
|
3688
|
+
if (tracked.variables) {
|
|
3689
|
+
return `${tracked.file} was pulled with --vars, so pushing it would replace the blueprint's variables with your values. Use --force to push it anyway.`;
|
|
3690
|
+
}
|
|
3691
|
+
return null;
|
|
3692
|
+
}
|
|
3637
3693
|
async function updateBlueprint(cwd, file, blueprintId, content, options, expectedChecksum) {
|
|
3638
3694
|
console.log(chalk6.cyan(`
|
|
3639
3695
|
\u{1F4E4} Updating blueprint ${chalk6.bold(blueprintId)}...`));
|
|
@@ -3924,6 +3980,11 @@ async function pushHierarchy(cwd, files, options) {
|
|
|
3924
3980
|
const content = fs.readFileSync(file.absolutePath, "utf-8");
|
|
3925
3981
|
const tracked = await findBlueprintByFile(cwd, file.path);
|
|
3926
3982
|
if (tracked) {
|
|
3983
|
+
const refusal = refusePush(cwd, tracked, options);
|
|
3984
|
+
if (refusal) {
|
|
3985
|
+
spinner.warn(`${file.path} \u2192 skipped: ${refusal}`);
|
|
3986
|
+
continue;
|
|
3987
|
+
}
|
|
3927
3988
|
const updateData = { content };
|
|
3928
3989
|
if (tracked.checksum && !options.force) {
|
|
3929
3990
|
updateData.expected_checksum = tracked.checksum;
|
|
@@ -6591,7 +6652,7 @@ var TEST_FRAMEWORKS2 = TEST_FRAMEWORKS;
|
|
|
6591
6652
|
|
|
6592
6653
|
// src/commands/wizard.ts
|
|
6593
6654
|
var DRAFTS_DIR = ".lynxprompt/drafts";
|
|
6594
|
-
var CLI_VERSION = "2.1.
|
|
6655
|
+
var CLI_VERSION = "2.1.40";
|
|
6595
6656
|
async function saveDraftLocally(name, config2, stepReached) {
|
|
6596
6657
|
const draftsPath = join3(process.cwd(), DRAFTS_DIR);
|
|
6597
6658
|
await mkdir3(draftsPath, { recursive: true });
|
|
@@ -10106,6 +10167,8 @@ async function statusCommand() {
|
|
|
10106
10167
|
console.log();
|
|
10107
10168
|
}
|
|
10108
10169
|
}
|
|
10170
|
+
console.log(chalk9.gray(" \u2713 means no local edits. Run 'lynxp sync --dry-run' to check the cloud too."));
|
|
10171
|
+
console.log();
|
|
10109
10172
|
}
|
|
10110
10173
|
console.log(chalk9.cyan("\u{1F4C4} AI Config Files"));
|
|
10111
10174
|
console.log();
|
|
@@ -10555,7 +10618,7 @@ async function diffCommand(fileOrId, options = {}) {
|
|
|
10555
10618
|
if (fileOrId) {
|
|
10556
10619
|
const tracked = await findBlueprintByFile(cwd, fileOrId);
|
|
10557
10620
|
if (tracked) {
|
|
10558
|
-
await diffFileWithBlueprint(cwd, fileOrId, tracked.id);
|
|
10621
|
+
await diffFileWithBlueprint(cwd, fileOrId, tracked.id, false, tracked.variables);
|
|
10559
10622
|
return;
|
|
10560
10623
|
}
|
|
10561
10624
|
await diffWithBlueprintId(cwd, fileOrId);
|
|
@@ -10581,7 +10644,7 @@ async function diffCommand(fileOrId, options = {}) {
|
|
|
10581
10644
|
}
|
|
10582
10645
|
console.log(chalk12.cyan(`\u{1F4C4} ${blueprint.file}`));
|
|
10583
10646
|
console.log(chalk12.gray(` Linked to: ${blueprint.name} (${blueprint.id})`));
|
|
10584
|
-
const result = await diffFileWithBlueprint(cwd, blueprint.file, blueprint.id, true);
|
|
10647
|
+
const result = await diffFileWithBlueprint(cwd, blueprint.file, blueprint.id, true, blueprint.variables);
|
|
10585
10648
|
if (result === "changed") {
|
|
10586
10649
|
hasChanges = true;
|
|
10587
10650
|
} else if (result === "same") {
|
|
@@ -10596,12 +10659,11 @@ async function diffCommand(fileOrId, options = {}) {
|
|
|
10596
10659
|
} else if (!hasChanges) {
|
|
10597
10660
|
console.log(chalk12.green("\u2713 All tracked files are in sync with their cloud blueprints!"));
|
|
10598
10661
|
} else {
|
|
10599
|
-
console.log(chalk12.gray("To
|
|
10600
|
-
console.log(chalk12.gray("To pull cloud changes: lynxp pull <id>"));
|
|
10662
|
+
console.log(chalk12.gray("To bring both sides together: lynxp sync"));
|
|
10601
10663
|
}
|
|
10602
10664
|
console.log();
|
|
10603
10665
|
}
|
|
10604
|
-
async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false) {
|
|
10666
|
+
async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false, variables) {
|
|
10605
10667
|
const filePath = join6(cwd, file);
|
|
10606
10668
|
if (!existsSync4(filePath)) {
|
|
10607
10669
|
console.log(chalk12.red(`\u2717 File not found: ${file}`));
|
|
@@ -10616,7 +10678,8 @@ async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false) {
|
|
|
10616
10678
|
return "error";
|
|
10617
10679
|
}
|
|
10618
10680
|
const localContent = await readFile7(filePath, "utf-8");
|
|
10619
|
-
const
|
|
10681
|
+
const cloudContent = variables ? fillBlueprintVariables(blueprint.content, variables).content : blueprint.content;
|
|
10682
|
+
const diff = computeDiff(cloudContent, localContent);
|
|
10620
10683
|
const stats = getDiffStats(diff);
|
|
10621
10684
|
if (stats.added === 0 && stats.removed === 0) {
|
|
10622
10685
|
if (!compact) {
|
|
@@ -10783,12 +10846,162 @@ async function diffLocal(cwd) {
|
|
|
10783
10846
|
console.log();
|
|
10784
10847
|
}
|
|
10785
10848
|
|
|
10786
|
-
// src/commands/
|
|
10849
|
+
// src/commands/sync.ts
|
|
10787
10850
|
import chalk13 from "chalk";
|
|
10851
|
+
import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
10852
|
+
import { existsSync as existsSync5 } from "fs";
|
|
10853
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
10854
|
+
async function syncCommand(fileArg, options = {}) {
|
|
10855
|
+
const cwd = process.cwd();
|
|
10856
|
+
if (!isAuthenticated()) {
|
|
10857
|
+
console.log(chalk13.yellow("Not logged in. Run 'lynxp login' to authenticate."));
|
|
10858
|
+
process.exit(1);
|
|
10859
|
+
}
|
|
10860
|
+
const { blueprints } = await loadBlueprints(cwd);
|
|
10861
|
+
const targets = fileArg ? blueprints.filter((b) => toTrackedPath(cwd, b.file) === toTrackedPath(cwd, fileArg)) : blueprints;
|
|
10862
|
+
if (targets.length === 0) {
|
|
10863
|
+
console.log(
|
|
10864
|
+
chalk13.yellow(fileArg ? `${fileArg} is not tracked.` : "No tracked blueprints found.")
|
|
10865
|
+
);
|
|
10866
|
+
console.log(chalk13.gray("Track a file with 'lynxp pull <id>' or 'lynxp link <file> <id>'."));
|
|
10867
|
+
if (fileArg) process.exit(1);
|
|
10868
|
+
return;
|
|
10869
|
+
}
|
|
10870
|
+
console.log();
|
|
10871
|
+
console.log(chalk13.cyan(options.dryRun ? "\u{1F431} LynxPrompt Sync (dry run)" : "\u{1F431} LynxPrompt Sync"));
|
|
10872
|
+
console.log();
|
|
10873
|
+
const counts = {
|
|
10874
|
+
"in-sync": 0,
|
|
10875
|
+
pulled: 0,
|
|
10876
|
+
pushed: 0,
|
|
10877
|
+
kept: 0,
|
|
10878
|
+
conflict: 0,
|
|
10879
|
+
error: 0
|
|
10880
|
+
};
|
|
10881
|
+
for (const tracked of targets) {
|
|
10882
|
+
counts[await syncOne(cwd, tracked, options)]++;
|
|
10883
|
+
}
|
|
10884
|
+
console.log();
|
|
10885
|
+
const parts = [
|
|
10886
|
+
`${counts.pulled} pulled`,
|
|
10887
|
+
`${counts.pushed} pushed`,
|
|
10888
|
+
`${counts["in-sync"]} in sync`
|
|
10889
|
+
];
|
|
10890
|
+
if (counts.kept) parts.push(`${counts.kept} local only`);
|
|
10891
|
+
if (counts.conflict) parts.push(`${counts.conflict} conflict`);
|
|
10892
|
+
if (counts.error) parts.push(`${counts.error} failed`);
|
|
10893
|
+
console.log(chalk13.gray(`${options.dryRun ? "Would be: " : ""}${parts.join(", ")}`));
|
|
10894
|
+
console.log();
|
|
10895
|
+
if (counts.conflict || counts.error) process.exitCode = 1;
|
|
10896
|
+
}
|
|
10897
|
+
async function syncOne(cwd, tracked, options) {
|
|
10898
|
+
const file = tracked.file;
|
|
10899
|
+
const filePath = join7(cwd, file);
|
|
10900
|
+
const remoteFile = conflictFilePath(cwd, file);
|
|
10901
|
+
const dry = options.dryRun === true;
|
|
10902
|
+
if (existsSync5(remoteFile)) {
|
|
10903
|
+
console.log(chalk13.red(`\u2717 ${file}: unresolved conflict`));
|
|
10904
|
+
console.log(chalk13.gray(` Merge ${file}.remote into ${file}, delete ${file}.remote, then run 'lynxp sync' again.`));
|
|
10905
|
+
return "conflict";
|
|
10906
|
+
}
|
|
10907
|
+
if (!existsSync5(filePath)) {
|
|
10908
|
+
console.log(chalk13.red(`\u2717 ${file}: file not found`));
|
|
10909
|
+
console.log(chalk13.gray(` Run 'lynxp pull ${tracked.id}' to restore it or 'lynxp unlink ${file}' to stop tracking it.`));
|
|
10910
|
+
return "error";
|
|
10911
|
+
}
|
|
10912
|
+
let remote;
|
|
10913
|
+
try {
|
|
10914
|
+
const { blueprint } = await api.getBlueprint(tracked.id);
|
|
10915
|
+
if (!blueprint?.content) {
|
|
10916
|
+
console.log(chalk13.red(`\u2717 ${file}: the cloud blueprint ${tracked.id} has no content`));
|
|
10917
|
+
return "error";
|
|
10918
|
+
}
|
|
10919
|
+
remote = blueprint.content;
|
|
10920
|
+
} catch (error) {
|
|
10921
|
+
console.log(chalk13.red(`\u2717 ${file}: could not fetch ${tracked.id}: ${errorText(error)}`));
|
|
10922
|
+
return "error";
|
|
10923
|
+
}
|
|
10924
|
+
const local = await readFile8(filePath, "utf-8");
|
|
10925
|
+
const rendered = tracked.variables ? fillBlueprintVariables(remote, tracked.variables).content : remote;
|
|
10926
|
+
const localChanged = calculateChecksum(local) !== (tracked.localChecksum ?? tracked.checksum);
|
|
10927
|
+
const remoteChanged = calculateChecksum(remote) !== tracked.checksum;
|
|
10928
|
+
if (calculateChecksum(local) === calculateChecksum(rendered)) {
|
|
10929
|
+
if (!dry && (localChanged || remoteChanged)) await record(cwd, tracked, remote, local);
|
|
10930
|
+
console.log(chalk13.green(`\u2713 ${file}: in sync`));
|
|
10931
|
+
return "in-sync";
|
|
10932
|
+
}
|
|
10933
|
+
if (!localChanged) {
|
|
10934
|
+
if (!dry) {
|
|
10935
|
+
await writeFile4(filePath, rendered, "utf-8");
|
|
10936
|
+
await record(cwd, tracked, remote, rendered);
|
|
10937
|
+
}
|
|
10938
|
+
console.log(chalk13.cyan(`\u2193 ${file}: ${dry ? "would update from the cloud" : "updated from the cloud"}`));
|
|
10939
|
+
return "pulled";
|
|
10940
|
+
}
|
|
10941
|
+
if (!remoteChanged) {
|
|
10942
|
+
const reason = tracked.variables ? "it was pulled with --vars, so pushing it would replace the blueprint's variables with your values" : !tracked.editable ? "marketplace blueprints are read-only" : null;
|
|
10943
|
+
if (reason) {
|
|
10944
|
+
console.log(chalk13.yellow(`\u25CF ${file}: local changes kept, not pushed (${reason})`));
|
|
10945
|
+
return "kept";
|
|
10946
|
+
}
|
|
10947
|
+
if (dry) {
|
|
10948
|
+
console.log(chalk13.cyan(`\u2191 ${file}: would push local changes`));
|
|
10949
|
+
return "pushed";
|
|
10950
|
+
}
|
|
10951
|
+
try {
|
|
10952
|
+
await api.updateBlueprint(tracked.id, { content: local, expected_checksum: tracked.checksum });
|
|
10953
|
+
await updateChecksum(cwd, file, local);
|
|
10954
|
+
console.log(chalk13.cyan(`\u2191 ${file}: pushed local changes`));
|
|
10955
|
+
return "pushed";
|
|
10956
|
+
} catch (error) {
|
|
10957
|
+
if (error instanceof ApiRequestError && error.statusCode === 409) {
|
|
10958
|
+
console.log(chalk13.red(`\u2717 ${file}: the cloud changed while syncing; run 'lynxp sync' again`));
|
|
10959
|
+
} else {
|
|
10960
|
+
console.log(chalk13.red(`\u2717 ${file}: push failed: ${errorText(error)}`));
|
|
10961
|
+
console.log(chalk13.gray(` Try 'lynxp push ${file}' for the full prompts.`));
|
|
10962
|
+
}
|
|
10963
|
+
return "error";
|
|
10964
|
+
}
|
|
10965
|
+
}
|
|
10966
|
+
if (!dry) {
|
|
10967
|
+
await mkdir4(dirname4(remoteFile), { recursive: true });
|
|
10968
|
+
await writeFile4(remoteFile, rendered, "utf-8");
|
|
10969
|
+
await record(cwd, tracked, remote, rendered);
|
|
10970
|
+
}
|
|
10971
|
+
console.log(chalk13.red(`\u2717 ${file}: changed both locally and in the cloud`));
|
|
10972
|
+
console.log(
|
|
10973
|
+
chalk13.gray(
|
|
10974
|
+
dry ? ` Sync would write the cloud version to ${file}.remote and leave ${file} as it is.` : ` Cloud version written to ${file}.remote; ${file} was not touched.
|
|
10975
|
+
Merge what you need into ${file}, delete ${file}.remote, then run 'lynxp sync' again.`
|
|
10976
|
+
)
|
|
10977
|
+
);
|
|
10978
|
+
return "conflict";
|
|
10979
|
+
}
|
|
10980
|
+
async function record(cwd, t, remote, written) {
|
|
10981
|
+
await trackBlueprint(cwd, {
|
|
10982
|
+
id: t.id,
|
|
10983
|
+
name: t.name,
|
|
10984
|
+
file: t.file,
|
|
10985
|
+
content: remote,
|
|
10986
|
+
source: t.source,
|
|
10987
|
+
version: t.version,
|
|
10988
|
+
hierarchyId: t.hierarchyId,
|
|
10989
|
+
hierarchyName: t.hierarchyName,
|
|
10990
|
+
repositoryPath: t.repositoryPath,
|
|
10991
|
+
variables: t.variables,
|
|
10992
|
+
localContent: t.variables ? written : void 0
|
|
10993
|
+
});
|
|
10994
|
+
}
|
|
10995
|
+
function errorText(error) {
|
|
10996
|
+
return error instanceof Error ? error.message : String(error);
|
|
10997
|
+
}
|
|
10998
|
+
|
|
10999
|
+
// src/commands/link.ts
|
|
11000
|
+
import chalk14 from "chalk";
|
|
10788
11001
|
import ora10 from "ora";
|
|
10789
11002
|
import prompts4 from "prompts";
|
|
10790
|
-
import { join as
|
|
10791
|
-
import { existsSync as
|
|
11003
|
+
import { join as join8 } from "path";
|
|
11004
|
+
import { existsSync as existsSync6 } from "fs";
|
|
10792
11005
|
function getSourceFromVisibility2(visibility) {
|
|
10793
11006
|
switch (visibility) {
|
|
10794
11007
|
case "PUBLIC":
|
|
@@ -10808,7 +11021,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10808
11021
|
return;
|
|
10809
11022
|
}
|
|
10810
11023
|
console.log();
|
|
10811
|
-
console.log(
|
|
11024
|
+
console.log(chalk14.cyan("\u{1F431} Link File to Blueprint"));
|
|
10812
11025
|
console.log();
|
|
10813
11026
|
let file;
|
|
10814
11027
|
let blueprintId = blueprintIdArg;
|
|
@@ -10822,13 +11035,13 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10822
11035
|
".rules",
|
|
10823
11036
|
".clinerules"
|
|
10824
11037
|
];
|
|
10825
|
-
const foundFiles = configFiles.filter((f) =>
|
|
11038
|
+
const foundFiles = configFiles.filter((f) => existsSync6(join8(cwd, f)));
|
|
10826
11039
|
if (foundFiles.length === 0) {
|
|
10827
|
-
console.log(
|
|
11040
|
+
console.log(chalk14.yellow("No AI configuration files found in this directory."));
|
|
10828
11041
|
console.log();
|
|
10829
|
-
console.log(
|
|
10830
|
-
console.log(
|
|
10831
|
-
console.log(
|
|
11042
|
+
console.log(chalk14.gray("Create one first:"));
|
|
11043
|
+
console.log(chalk14.gray(" lynxp wizard Generate a new config file"));
|
|
11044
|
+
console.log(chalk14.gray(" lynxp pull <id> Download from marketplace"));
|
|
10832
11045
|
return;
|
|
10833
11046
|
}
|
|
10834
11047
|
const { selectedFile } = await prompts4({
|
|
@@ -10842,22 +11055,22 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10842
11055
|
}))
|
|
10843
11056
|
});
|
|
10844
11057
|
if (!selectedFile) {
|
|
10845
|
-
console.log(
|
|
11058
|
+
console.log(chalk14.gray("Cancelled."));
|
|
10846
11059
|
return;
|
|
10847
11060
|
}
|
|
10848
11061
|
file = selectedFile;
|
|
10849
11062
|
} else {
|
|
10850
11063
|
file = fileArg;
|
|
10851
11064
|
}
|
|
10852
|
-
const filePath =
|
|
10853
|
-
if (!
|
|
10854
|
-
console.log(
|
|
11065
|
+
const filePath = join8(cwd, file);
|
|
11066
|
+
if (!existsSync6(filePath)) {
|
|
11067
|
+
console.log(chalk14.red(`\u2717 File not found: ${file}`));
|
|
10855
11068
|
return;
|
|
10856
11069
|
}
|
|
10857
11070
|
const existing = await findBlueprintByFile(cwd, file);
|
|
10858
11071
|
if (existing) {
|
|
10859
|
-
console.log(
|
|
10860
|
-
console.log(
|
|
11072
|
+
console.log(chalk14.yellow(`This file is already linked to: ${existing.name}`));
|
|
11073
|
+
console.log(chalk14.gray(` ID: ${existing.id}`));
|
|
10861
11074
|
console.log();
|
|
10862
11075
|
const { proceed } = await prompts4({
|
|
10863
11076
|
type: "confirm",
|
|
@@ -10866,13 +11079,13 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10866
11079
|
initial: false
|
|
10867
11080
|
});
|
|
10868
11081
|
if (!proceed) {
|
|
10869
|
-
console.log(
|
|
11082
|
+
console.log(chalk14.gray("Cancelled."));
|
|
10870
11083
|
return;
|
|
10871
11084
|
}
|
|
10872
11085
|
}
|
|
10873
11086
|
if (!blueprintId) {
|
|
10874
11087
|
if (!isAuthenticated()) {
|
|
10875
|
-
console.log(
|
|
11088
|
+
console.log(chalk14.yellow("You need to login to access your blueprints."));
|
|
10876
11089
|
const { doLogin } = await prompts4({
|
|
10877
11090
|
type: "confirm",
|
|
10878
11091
|
name: "doLogin",
|
|
@@ -10880,10 +11093,10 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10880
11093
|
initial: true
|
|
10881
11094
|
});
|
|
10882
11095
|
if (doLogin) {
|
|
10883
|
-
console.log(
|
|
11096
|
+
console.log(chalk14.gray("Run 'lynxp login' in another terminal, then come back here."));
|
|
10884
11097
|
return;
|
|
10885
11098
|
}
|
|
10886
|
-
console.log(
|
|
11099
|
+
console.log(chalk14.gray("Cancelled."));
|
|
10887
11100
|
return;
|
|
10888
11101
|
}
|
|
10889
11102
|
const { searchMethod } = await prompts4({
|
|
@@ -10897,7 +11110,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10897
11110
|
]
|
|
10898
11111
|
});
|
|
10899
11112
|
if (!searchMethod) {
|
|
10900
|
-
console.log(
|
|
11113
|
+
console.log(chalk14.gray("Cancelled."));
|
|
10901
11114
|
return;
|
|
10902
11115
|
}
|
|
10903
11116
|
if (searchMethod === "list") {
|
|
@@ -10906,8 +11119,8 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10906
11119
|
const blueprints = await api.listAllBlueprints();
|
|
10907
11120
|
spinner2.stop();
|
|
10908
11121
|
if (!blueprints || blueprints.length === 0) {
|
|
10909
|
-
console.log(
|
|
10910
|
-
console.log(
|
|
11122
|
+
console.log(chalk14.yellow("You don't have any blueprints yet."));
|
|
11123
|
+
console.log(chalk14.gray("Create one with 'lynxp push' or search the marketplace."));
|
|
10911
11124
|
return;
|
|
10912
11125
|
}
|
|
10913
11126
|
const { selected } = await prompts4({
|
|
@@ -10921,13 +11134,13 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10921
11134
|
}))
|
|
10922
11135
|
});
|
|
10923
11136
|
if (!selected) {
|
|
10924
|
-
console.log(
|
|
11137
|
+
console.log(chalk14.gray("Cancelled."));
|
|
10925
11138
|
return;
|
|
10926
11139
|
}
|
|
10927
11140
|
blueprintId = selected;
|
|
10928
11141
|
} catch {
|
|
10929
11142
|
spinner2.stop();
|
|
10930
|
-
console.log(
|
|
11143
|
+
console.log(chalk14.red("\u2717 Could not fetch blueprints"));
|
|
10931
11144
|
return;
|
|
10932
11145
|
}
|
|
10933
11146
|
} else if (searchMethod === "search") {
|
|
@@ -10937,7 +11150,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10937
11150
|
message: "Search for:"
|
|
10938
11151
|
});
|
|
10939
11152
|
if (!query) {
|
|
10940
|
-
console.log(
|
|
11153
|
+
console.log(chalk14.gray("Cancelled."));
|
|
10941
11154
|
return;
|
|
10942
11155
|
}
|
|
10943
11156
|
const spinner2 = ora10(`Searching for "${query}"...`).start();
|
|
@@ -10945,7 +11158,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10945
11158
|
const results = await api.searchBlueprints(query, 10);
|
|
10946
11159
|
spinner2.stop();
|
|
10947
11160
|
if (!results.templates || results.templates.length === 0) {
|
|
10948
|
-
console.log(
|
|
11161
|
+
console.log(chalk14.yellow(`No blueprints found for "${query}"`));
|
|
10949
11162
|
return;
|
|
10950
11163
|
}
|
|
10951
11164
|
const { selected } = await prompts4({
|
|
@@ -10959,13 +11172,13 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10959
11172
|
}))
|
|
10960
11173
|
});
|
|
10961
11174
|
if (!selected) {
|
|
10962
|
-
console.log(
|
|
11175
|
+
console.log(chalk14.gray("Cancelled."));
|
|
10963
11176
|
return;
|
|
10964
11177
|
}
|
|
10965
11178
|
blueprintId = selected;
|
|
10966
11179
|
} catch {
|
|
10967
11180
|
spinner2.stop();
|
|
10968
|
-
console.log(
|
|
11181
|
+
console.log(chalk14.red("\u2717 Search failed"));
|
|
10969
11182
|
return;
|
|
10970
11183
|
}
|
|
10971
11184
|
} else {
|
|
@@ -10975,82 +11188,82 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
10975
11188
|
message: "Enter blueprint ID:"
|
|
10976
11189
|
});
|
|
10977
11190
|
if (!manualId) {
|
|
10978
|
-
console.log(
|
|
11191
|
+
console.log(chalk14.gray("Cancelled."));
|
|
10979
11192
|
return;
|
|
10980
11193
|
}
|
|
10981
11194
|
blueprintId = manualId;
|
|
10982
11195
|
}
|
|
10983
11196
|
}
|
|
10984
11197
|
if (!blueprintId) {
|
|
10985
|
-
console.log(
|
|
11198
|
+
console.log(chalk14.red("\u2717 No blueprint ID provided."));
|
|
10986
11199
|
return;
|
|
10987
11200
|
}
|
|
10988
|
-
const spinner = ora10(`Fetching blueprint ${
|
|
11201
|
+
const spinner = ora10(`Fetching blueprint ${chalk14.cyan(blueprintId)}...`).start();
|
|
10989
11202
|
try {
|
|
10990
11203
|
const { blueprint } = await api.getBlueprint(blueprintId);
|
|
10991
11204
|
spinner.stop();
|
|
10992
11205
|
const source = getSourceFromVisibility2(blueprint.visibility);
|
|
10993
11206
|
const isMarketplace = source === "marketplace";
|
|
10994
11207
|
console.log();
|
|
10995
|
-
console.log(
|
|
11208
|
+
console.log(chalk14.cyan(`\u{1F431} Blueprint: ${chalk14.bold(safeText(blueprint.name))}`));
|
|
10996
11209
|
if (blueprint.description) {
|
|
10997
|
-
console.log(
|
|
11210
|
+
console.log(chalk14.gray(` ${safeText(blueprint.description)}`));
|
|
10998
11211
|
}
|
|
10999
|
-
console.log(
|
|
11212
|
+
console.log(chalk14.gray(` Visibility: ${blueprint.visibility}`));
|
|
11000
11213
|
console.log();
|
|
11001
11214
|
if (isMarketplace) {
|
|
11002
|
-
console.log(
|
|
11003
|
-
console.log(
|
|
11004
|
-
console.log(
|
|
11215
|
+
console.log(chalk14.yellow("\u26A0 This is a marketplace blueprint."));
|
|
11216
|
+
console.log(chalk14.gray(" Your local changes will NOT sync back to the cloud."));
|
|
11217
|
+
console.log(chalk14.gray(" To make changes, you'll need to create your own copy."));
|
|
11005
11218
|
console.log();
|
|
11006
11219
|
}
|
|
11007
11220
|
const { confirm } = await prompts4({
|
|
11008
11221
|
type: "confirm",
|
|
11009
11222
|
name: "confirm",
|
|
11010
|
-
message: `Link ${
|
|
11223
|
+
message: `Link ${chalk14.cyan(file)} to ${chalk14.cyan(blueprint.name)}?`,
|
|
11011
11224
|
initial: true
|
|
11012
11225
|
});
|
|
11013
11226
|
if (!confirm) {
|
|
11014
|
-
console.log(
|
|
11227
|
+
console.log(chalk14.gray("Cancelled."));
|
|
11015
11228
|
return;
|
|
11016
11229
|
}
|
|
11017
11230
|
await linkBlueprint(cwd, file, blueprint.id, blueprint.name, source, blueprint.content);
|
|
11018
11231
|
console.log();
|
|
11019
|
-
console.log(
|
|
11232
|
+
console.log(chalk14.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
|
|
11020
11233
|
console.log();
|
|
11021
|
-
console.log(
|
|
11022
|
-
console.log(
|
|
11023
|
-
console.log(
|
|
11234
|
+
console.log(chalk14.gray("Next steps:"));
|
|
11235
|
+
console.log(chalk14.gray(` \u2022 Run 'lynxp diff' to see differences`));
|
|
11236
|
+
console.log(chalk14.gray(` \u2022 Run 'lynxp status' to see all tracked blueprints`));
|
|
11024
11237
|
if (!isMarketplace) {
|
|
11025
|
-
console.log(
|
|
11238
|
+
console.log(chalk14.gray(` \u2022 Run 'lynxp push' to push local changes to cloud`));
|
|
11026
11239
|
}
|
|
11027
11240
|
console.log();
|
|
11028
11241
|
} catch (error) {
|
|
11029
11242
|
spinner.stop();
|
|
11030
11243
|
if (error instanceof ApiRequestError) {
|
|
11031
11244
|
if (error.statusCode === 404) {
|
|
11032
|
-
console.log(
|
|
11033
|
-
console.log(
|
|
11245
|
+
console.log(chalk14.red(`\u2717 Blueprint not found: ${blueprintId}`));
|
|
11246
|
+
console.log(chalk14.gray(" Make sure the ID is correct. Use 'lynxp list' or 'lynxp search' to find blueprints."));
|
|
11034
11247
|
} else if (error.statusCode === 403) {
|
|
11035
|
-
console.log(
|
|
11248
|
+
console.log(chalk14.red("\u2717 You don't have access to this blueprint."));
|
|
11036
11249
|
} else {
|
|
11037
|
-
console.log(
|
|
11250
|
+
console.log(chalk14.red(`\u2717 Error: ${error.message}`));
|
|
11038
11251
|
}
|
|
11039
11252
|
} else {
|
|
11040
|
-
console.log(
|
|
11253
|
+
console.log(chalk14.red("\u2717 An unexpected error occurred."));
|
|
11041
11254
|
}
|
|
11042
11255
|
}
|
|
11043
11256
|
}
|
|
11044
11257
|
async function unlinkCommand(fileArg) {
|
|
11045
11258
|
const cwd = process.cwd();
|
|
11046
11259
|
console.log();
|
|
11047
|
-
console.log(
|
|
11260
|
+
console.log(chalk14.cyan("\u{1F431} Unlink File from Blueprint"));
|
|
11048
11261
|
console.log();
|
|
11049
11262
|
let file;
|
|
11050
11263
|
if (!fileArg) {
|
|
11051
11264
|
const status = await checkSyncStatus(cwd);
|
|
11052
11265
|
if (status.length === 0) {
|
|
11053
|
-
console.log(
|
|
11266
|
+
console.log(chalk14.yellow("No files are currently linked to blueprints."));
|
|
11054
11267
|
return;
|
|
11055
11268
|
}
|
|
11056
11269
|
const { selectedFile } = await prompts4({
|
|
@@ -11064,7 +11277,7 @@ async function unlinkCommand(fileArg) {
|
|
|
11064
11277
|
}))
|
|
11065
11278
|
});
|
|
11066
11279
|
if (!selectedFile) {
|
|
11067
|
-
console.log(
|
|
11280
|
+
console.log(chalk14.gray("Cancelled."));
|
|
11068
11281
|
return;
|
|
11069
11282
|
}
|
|
11070
11283
|
file = selectedFile;
|
|
@@ -11073,77 +11286,77 @@ async function unlinkCommand(fileArg) {
|
|
|
11073
11286
|
}
|
|
11074
11287
|
const tracked = await findBlueprintByFile(cwd, file);
|
|
11075
11288
|
if (!tracked) {
|
|
11076
|
-
console.log(
|
|
11289
|
+
console.log(chalk14.yellow(`File is not linked to any blueprint: ${file}`));
|
|
11077
11290
|
return;
|
|
11078
11291
|
}
|
|
11079
|
-
console.log(
|
|
11080
|
-
console.log(
|
|
11081
|
-
console.log(
|
|
11292
|
+
console.log(chalk14.gray(`Currently linked to: ${tracked.name}`));
|
|
11293
|
+
console.log(chalk14.gray(` ID: ${tracked.id}`));
|
|
11294
|
+
console.log(chalk14.gray(` Source: ${tracked.source}`));
|
|
11082
11295
|
console.log();
|
|
11083
11296
|
const { confirm } = await prompts4({
|
|
11084
11297
|
type: "confirm",
|
|
11085
11298
|
name: "confirm",
|
|
11086
|
-
message: `Unlink ${
|
|
11299
|
+
message: `Unlink ${chalk14.cyan(file)} from ${chalk14.cyan(tracked.name)}?`,
|
|
11087
11300
|
initial: true
|
|
11088
11301
|
});
|
|
11089
11302
|
if (!confirm) {
|
|
11090
|
-
console.log(
|
|
11303
|
+
console.log(chalk14.gray("Cancelled."));
|
|
11091
11304
|
return;
|
|
11092
11305
|
}
|
|
11093
11306
|
const success = await untrackBlueprint(cwd, file);
|
|
11094
11307
|
if (success) {
|
|
11095
11308
|
console.log();
|
|
11096
|
-
console.log(
|
|
11097
|
-
console.log(
|
|
11309
|
+
console.log(chalk14.green(`\u2705 Unlinked: ${file}`));
|
|
11310
|
+
console.log(chalk14.gray(" The file is now standalone. Changes won't sync with the cloud."));
|
|
11098
11311
|
console.log();
|
|
11099
11312
|
} else {
|
|
11100
|
-
console.log(
|
|
11313
|
+
console.log(chalk14.red("\u2717 Failed to unlink file."));
|
|
11101
11314
|
}
|
|
11102
11315
|
}
|
|
11103
11316
|
async function listTrackedBlueprints(cwd) {
|
|
11104
11317
|
console.log();
|
|
11105
|
-
console.log(
|
|
11318
|
+
console.log(chalk14.cyan("\u{1F431} Tracked Blueprints"));
|
|
11106
11319
|
console.log();
|
|
11107
11320
|
const status = await checkSyncStatus(cwd);
|
|
11108
11321
|
if (status.length === 0) {
|
|
11109
|
-
console.log(
|
|
11322
|
+
console.log(chalk14.gray("No blueprints are currently tracked."));
|
|
11110
11323
|
console.log();
|
|
11111
|
-
console.log(
|
|
11112
|
-
console.log(
|
|
11113
|
-
console.log(
|
|
11324
|
+
console.log(chalk14.gray("To track a blueprint:"));
|
|
11325
|
+
console.log(chalk14.gray(" lynxp pull <blueprint-id> Download and track a blueprint"));
|
|
11326
|
+
console.log(chalk14.gray(" lynxp link Link an existing file to a blueprint"));
|
|
11114
11327
|
return;
|
|
11115
11328
|
}
|
|
11116
11329
|
for (const { blueprint, localModified, fileExists: fileExists2 } of status) {
|
|
11117
|
-
const statusIcon = !fileExists2 ?
|
|
11330
|
+
const statusIcon = !fileExists2 ? chalk14.red("\u2717") : localModified ? chalk14.yellow("\u25CF") : chalk14.green("\u2713");
|
|
11118
11331
|
const sourceLabel = {
|
|
11119
|
-
marketplace:
|
|
11120
|
-
team:
|
|
11121
|
-
private:
|
|
11122
|
-
local:
|
|
11332
|
+
marketplace: chalk14.gray("[marketplace]"),
|
|
11333
|
+
team: chalk14.blue("[team]"),
|
|
11334
|
+
private: chalk14.green("[private]"),
|
|
11335
|
+
local: chalk14.gray("[local]")
|
|
11123
11336
|
}[blueprint.source];
|
|
11124
|
-
console.log(`${statusIcon} ${
|
|
11337
|
+
console.log(`${statusIcon} ${chalk14.cyan(blueprint.file)}`);
|
|
11125
11338
|
console.log(` ${sourceLabel} ${blueprint.name}`);
|
|
11126
|
-
console.log(` ${
|
|
11339
|
+
console.log(` ${chalk14.gray(`ID: ${blueprint.id}`)}`);
|
|
11127
11340
|
if (!fileExists2) {
|
|
11128
|
-
console.log(
|
|
11341
|
+
console.log(chalk14.red(` \u26A0 File not found`));
|
|
11129
11342
|
} else if (localModified) {
|
|
11130
|
-
console.log(
|
|
11343
|
+
console.log(chalk14.yellow(` \u26A0 Local changes detected`));
|
|
11131
11344
|
}
|
|
11132
11345
|
console.log();
|
|
11133
11346
|
}
|
|
11134
|
-
console.log(
|
|
11135
|
-
console.log(
|
|
11347
|
+
console.log(chalk14.gray("Legend:"));
|
|
11348
|
+
console.log(chalk14.gray(` ${chalk14.green("\u2713")} In sync ${chalk14.yellow("\u25CF")} Modified locally ${chalk14.red("\u2717")} Missing`));
|
|
11136
11349
|
console.log();
|
|
11137
11350
|
}
|
|
11138
11351
|
|
|
11139
11352
|
// src/commands/analyze.ts
|
|
11140
|
-
import
|
|
11353
|
+
import chalk15 from "chalk";
|
|
11141
11354
|
import ora11 from "ora";
|
|
11142
11355
|
async function analyzeCommand(options) {
|
|
11143
11356
|
console.log();
|
|
11144
11357
|
if (!options.json) {
|
|
11145
|
-
console.log(
|
|
11146
|
-
console.log(
|
|
11358
|
+
console.log(chalk15.cyan.bold(" \u{1F50D} LynxPrompt Analyzer"));
|
|
11359
|
+
console.log(chalk15.gray(" Detect project configuration and tech stack"));
|
|
11147
11360
|
console.log();
|
|
11148
11361
|
}
|
|
11149
11362
|
let detected;
|
|
@@ -11151,7 +11364,7 @@ async function analyzeCommand(options) {
|
|
|
11151
11364
|
if (options.remote) {
|
|
11152
11365
|
if (!isGitUrl(options.remote)) {
|
|
11153
11366
|
if (!options.json) {
|
|
11154
|
-
console.log(
|
|
11367
|
+
console.log(chalk15.red(" \u2717 Invalid Git URL provided"));
|
|
11155
11368
|
}
|
|
11156
11369
|
process.exit(1);
|
|
11157
11370
|
}
|
|
@@ -11164,7 +11377,7 @@ async function analyzeCommand(options) {
|
|
|
11164
11377
|
spinner.succeed("Remote repository analyzed");
|
|
11165
11378
|
} else {
|
|
11166
11379
|
spinner.fail("Could not analyze repository");
|
|
11167
|
-
console.log(
|
|
11380
|
+
console.log(chalk15.gray(" The repository may be private or inaccessible."));
|
|
11168
11381
|
process.exit(1);
|
|
11169
11382
|
}
|
|
11170
11383
|
} else {
|
|
@@ -11197,57 +11410,57 @@ async function analyzeCommand(options) {
|
|
|
11197
11410
|
}
|
|
11198
11411
|
console.log();
|
|
11199
11412
|
if (!detected) {
|
|
11200
|
-
console.log(
|
|
11413
|
+
console.log(chalk15.yellow(" No project configuration detected."));
|
|
11201
11414
|
console.log();
|
|
11202
|
-
console.log(
|
|
11203
|
-
console.log(
|
|
11204
|
-
console.log(
|
|
11415
|
+
console.log(chalk15.gray(" Tips:"));
|
|
11416
|
+
console.log(chalk15.gray(" \u2022 Make sure you're in a project directory with a package.json, pyproject.toml, etc."));
|
|
11417
|
+
console.log(chalk15.gray(" \u2022 Use --remote <url> to analyze a remote repository"));
|
|
11205
11418
|
console.log();
|
|
11206
11419
|
return;
|
|
11207
11420
|
}
|
|
11208
|
-
console.log(
|
|
11421
|
+
console.log(chalk15.green.bold(" \u{1F4CA} Project Analysis"));
|
|
11209
11422
|
console.log();
|
|
11210
|
-
console.log(
|
|
11211
|
-
console.log(
|
|
11212
|
-
console.log(` ${
|
|
11423
|
+
console.log(chalk15.white(" Basic Info"));
|
|
11424
|
+
console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
11425
|
+
console.log(` ${chalk15.dim("Name:")} ${chalk15.white(detected.name || "unknown")}`);
|
|
11213
11426
|
if (detected.description) {
|
|
11214
|
-
console.log(` ${
|
|
11427
|
+
console.log(` ${chalk15.dim("Description:")} ${chalk15.gray(detected.description)}`);
|
|
11215
11428
|
}
|
|
11216
|
-
console.log(` ${
|
|
11429
|
+
console.log(` ${chalk15.dim("Type:")} ${chalk15.white(detected.type || "application")}`);
|
|
11217
11430
|
if (detected.license) {
|
|
11218
|
-
console.log(` ${
|
|
11431
|
+
console.log(` ${chalk15.dim("License:")} ${chalk15.white(detected.license.toUpperCase())}`);
|
|
11219
11432
|
}
|
|
11220
11433
|
console.log();
|
|
11221
|
-
console.log(
|
|
11222
|
-
console.log(
|
|
11434
|
+
console.log(chalk15.white(" Tech Stack"));
|
|
11435
|
+
console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
11223
11436
|
if (detected.stack.length > 0) {
|
|
11224
11437
|
for (const tech of detected.stack) {
|
|
11225
|
-
console.log(` ${
|
|
11438
|
+
console.log(` ${chalk15.cyan("\u2022")} ${tech}`);
|
|
11226
11439
|
}
|
|
11227
11440
|
} else {
|
|
11228
|
-
console.log(
|
|
11441
|
+
console.log(chalk15.gray(" No tech stack detected"));
|
|
11229
11442
|
}
|
|
11230
11443
|
console.log();
|
|
11231
|
-
console.log(
|
|
11232
|
-
console.log(
|
|
11233
|
-
console.log(` ${
|
|
11234
|
-
console.log(` ${
|
|
11235
|
-
console.log(` ${
|
|
11236
|
-
console.log(` ${
|
|
11444
|
+
console.log(chalk15.white(" Infrastructure"));
|
|
11445
|
+
console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
11446
|
+
console.log(` ${chalk15.dim("Package Manager:")} ${detected.packageManager || chalk15.gray("none detected")}`);
|
|
11447
|
+
console.log(` ${chalk15.dim("Repo Host:")} ${detected.repoHost || chalk15.gray("none detected")}`);
|
|
11448
|
+
console.log(` ${chalk15.dim("CI/CD:")} ${detected.cicd || chalk15.gray("none detected")}`);
|
|
11449
|
+
console.log(` ${chalk15.dim("Docker:")} ${detected.hasDocker ? chalk15.green("yes") : chalk15.gray("no")}`);
|
|
11237
11450
|
console.log();
|
|
11238
11451
|
if (detected.commands && Object.keys(detected.commands).length > 0) {
|
|
11239
|
-
console.log(
|
|
11240
|
-
console.log(
|
|
11452
|
+
console.log(chalk15.white(" Commands"));
|
|
11453
|
+
console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
11241
11454
|
for (const [key, value] of Object.entries(detected.commands)) {
|
|
11242
11455
|
if (value) {
|
|
11243
11456
|
const cmdValue = Array.isArray(value) ? value.join(", ") : value;
|
|
11244
|
-
console.log(` ${
|
|
11457
|
+
console.log(` ${chalk15.dim(key.padEnd(10))} ${chalk15.yellow(cmdValue)}`);
|
|
11245
11458
|
}
|
|
11246
11459
|
}
|
|
11247
11460
|
console.log();
|
|
11248
11461
|
}
|
|
11249
|
-
console.log(
|
|
11250
|
-
console.log(
|
|
11462
|
+
console.log(chalk15.white(" \u{1F4A1} Recommendations"));
|
|
11463
|
+
console.log(chalk15.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
11251
11464
|
const recommendations = [];
|
|
11252
11465
|
if (!detected.cicd) {
|
|
11253
11466
|
recommendations.push("Add CI/CD configuration (GitHub Actions, GitLab CI, etc.)");
|
|
@@ -11266,122 +11479,81 @@ async function analyzeCommand(options) {
|
|
|
11266
11479
|
}
|
|
11267
11480
|
if (recommendations.length > 0) {
|
|
11268
11481
|
for (const rec of recommendations) {
|
|
11269
|
-
console.log(` ${
|
|
11482
|
+
console.log(` ${chalk15.yellow("\u2022")} ${rec}`);
|
|
11270
11483
|
}
|
|
11271
11484
|
} else {
|
|
11272
|
-
console.log(
|
|
11485
|
+
console.log(chalk15.green(" \u2713 Project looks well-configured!"));
|
|
11273
11486
|
}
|
|
11274
11487
|
console.log();
|
|
11275
|
-
console.log(
|
|
11488
|
+
console.log(chalk15.gray(" Run 'lynxp wizard' to generate AI IDE configuration"));
|
|
11276
11489
|
console.log();
|
|
11277
11490
|
}
|
|
11278
11491
|
|
|
11279
11492
|
// src/commands/convert.ts
|
|
11280
|
-
import
|
|
11281
|
-
import { readFile as
|
|
11282
|
-
import {
|
|
11493
|
+
import chalk16 from "chalk";
|
|
11494
|
+
import { readFile as readFile9, writeFile as writeFile5, access as access6 } from "fs/promises";
|
|
11495
|
+
import { basename as basename2, resolve as resolve2, dirname as dirname5 } from "path";
|
|
11283
11496
|
import ora12 from "ora";
|
|
11284
11497
|
|
|
11285
11498
|
// src/utils/convert-format.ts
|
|
11286
|
-
|
|
11499
|
+
import { access as access5 } from "fs/promises";
|
|
11500
|
+
import { join as join9 } from "path";
|
|
11501
|
+
|
|
11502
|
+
// ../packages/shared/src/rules-format.ts
|
|
11503
|
+
function yamlString(value) {
|
|
11504
|
+
return JSON.stringify(value);
|
|
11505
|
+
}
|
|
11506
|
+
var cursorFrontmatter = (description) => [`description: ${yamlString(description)}`, "alwaysApply: true"];
|
|
11507
|
+
var PLATFORM_FRONTMATTER = {
|
|
11508
|
+
cursor: cursorFrontmatter,
|
|
11509
|
+
firebender: cursorFrontmatter,
|
|
11510
|
+
// Firebender reads Cursor-style .mdc rules
|
|
11511
|
+
kiro: () => ["inclusion: always"],
|
|
11512
|
+
augment: (description) => ["type: always_apply", `description: ${yamlString(description)}`]
|
|
11513
|
+
};
|
|
11514
|
+
var FRONTMATTER_PATTERN = /^---\r?\n[\s\S]*?\r?\n---[ \t]*(?:\r?\n)+/;
|
|
11515
|
+
function frontmatterLines(platform2, description) {
|
|
11516
|
+
const keys = PLATFORM_FRONTMATTER[platform2];
|
|
11517
|
+
return keys ? ["---", ...keys(description), "---", ""] : [];
|
|
11518
|
+
}
|
|
11287
11519
|
function splitFrontmatter(content) {
|
|
11288
|
-
const match = content.match(
|
|
11289
|
-
if (!match) return { frontmatter:
|
|
11290
|
-
return { frontmatter: match[
|
|
11291
|
-
}
|
|
11292
|
-
function
|
|
11293
|
-
const config2 = {};
|
|
11294
|
-
const sections = content.split(/^##\s+/m);
|
|
11295
|
-
for (const section of sections) {
|
|
11296
|
-
const lines = section.trim().split("\n");
|
|
11297
|
-
if (lines.length === 0) continue;
|
|
11298
|
-
const title = lines[0].toLowerCase().trim();
|
|
11299
|
-
const body2 = lines.slice(1).join("\n").trim();
|
|
11300
|
-
config2[title] = body2;
|
|
11301
|
-
}
|
|
11302
|
-
config2._raw = content;
|
|
11520
|
+
const match = content.match(FRONTMATTER_PATTERN);
|
|
11521
|
+
if (!match) return { frontmatter: "", body: content };
|
|
11522
|
+
return { frontmatter: match[0], body: content.slice(match[0].length) };
|
|
11523
|
+
}
|
|
11524
|
+
function adaptRulesContent(content, fromPlatform, toPlatform, description) {
|
|
11303
11525
|
const { frontmatter, body } = splitFrontmatter(content);
|
|
11304
|
-
|
|
11305
|
-
if (frontmatter
|
|
11306
|
-
return
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
alwaysApply: true`;
|
|
11311
|
-
function generateTargetContent(config2, targetPlatform) {
|
|
11312
|
-
const rawContent = config2._body ?? config2._raw ?? "";
|
|
11313
|
-
const sourceFrontmatter = config2._frontmatter;
|
|
11314
|
-
const mdcFrontmatter = sourceFrontmatter && /^(alwaysApply|globs)\s*:/m.test(sourceFrontmatter) ? sourceFrontmatter : DEFAULT_MDC_FRONTMATTER;
|
|
11315
|
-
if (isCommandTarget(targetPlatform)) {
|
|
11316
|
-
return config2._raw || "";
|
|
11317
|
-
}
|
|
11318
|
-
switch (targetPlatform) {
|
|
11319
|
-
case "cursor":
|
|
11320
|
-
case "firebender":
|
|
11321
|
-
return `---
|
|
11322
|
-
${mdcFrontmatter}
|
|
11323
|
-
---
|
|
11324
|
-
|
|
11325
|
-
${rawContent}`;
|
|
11326
|
-
case "kiro":
|
|
11327
|
-
return `---
|
|
11328
|
-
inclusion: always
|
|
11329
|
-
---
|
|
11330
|
-
|
|
11331
|
-
${rawContent}`;
|
|
11332
|
-
case "augment":
|
|
11333
|
-
return `---
|
|
11334
|
-
type: always_apply
|
|
11335
|
-
---
|
|
11336
|
-
|
|
11337
|
-
${rawContent}`;
|
|
11338
|
-
case "cursor_legacy":
|
|
11339
|
-
return rawContent.replace(/^#\s+/gm, "").replace(/^##\s+/gm, "\n").replace(/^###\s+/gm, "").trim();
|
|
11340
|
-
case "windsurf":
|
|
11341
|
-
case "cline":
|
|
11342
|
-
case "goose":
|
|
11343
|
-
case "trae":
|
|
11344
|
-
return rawContent;
|
|
11345
|
-
case "tabnine":
|
|
11346
|
-
return `# Tabnine configuration
|
|
11347
|
-
# Converted from AI IDE configuration
|
|
11348
|
-
instructions: |
|
|
11349
|
-
${rawContent.split("\n").map((line) => ` ${line}`).join("\n")}
|
|
11350
|
-
`;
|
|
11351
|
-
case "void":
|
|
11352
|
-
case "continue":
|
|
11353
|
-
case "cody":
|
|
11354
|
-
case "codegpt":
|
|
11355
|
-
case "supermaven":
|
|
11356
|
-
return JSON.stringify({ instructions: rawContent }, null, 2) + "\n";
|
|
11357
|
-
case "copilot":
|
|
11358
|
-
return `# GitHub Copilot Instructions
|
|
11359
|
-
|
|
11360
|
-
${rawContent}
|
|
11361
|
-
`;
|
|
11362
|
-
case "agents":
|
|
11363
|
-
case "universal":
|
|
11364
|
-
case "claude":
|
|
11365
|
-
case "codex":
|
|
11366
|
-
case "antigravity":
|
|
11367
|
-
case "gemini-cli":
|
|
11368
|
-
case "zed":
|
|
11369
|
-
case "amazonq":
|
|
11370
|
-
case "junie":
|
|
11371
|
-
case "warp":
|
|
11372
|
-
case "openhands":
|
|
11373
|
-
case "crush":
|
|
11374
|
-
case "aider":
|
|
11375
|
-
case "opencode":
|
|
11376
|
-
case "firebase":
|
|
11377
|
-
case "roocode":
|
|
11378
|
-
case "kilocode":
|
|
11379
|
-
default:
|
|
11380
|
-
return rawContent;
|
|
11381
|
-
}
|
|
11526
|
+
const target = PLATFORM_FRONTMATTER[toPlatform];
|
|
11527
|
+
if (fromPlatform === toPlatform && (frontmatter || !target)) return content;
|
|
11528
|
+
if (frontmatter && target && PLATFORM_FRONTMATTER[fromPlatform] === target) return content;
|
|
11529
|
+
const lines = frontmatterLines(toPlatform, description);
|
|
11530
|
+
return lines.length ? `${lines.join("\n")}
|
|
11531
|
+
${body}` : body;
|
|
11382
11532
|
}
|
|
11383
11533
|
|
|
11384
|
-
// src/
|
|
11534
|
+
// src/utils/convert-format.ts
|
|
11535
|
+
var isCommandTarget = (target) => target.includes("-command") || target.includes("-prompt") || target.includes("-workflow");
|
|
11536
|
+
var RULES_DESCRIPTION = "AI rules for this project";
|
|
11537
|
+
function isStructuredPlatform(platform2) {
|
|
11538
|
+
const format = getPlatform(platform2)?.format;
|
|
11539
|
+
return format === "json" || format === "yaml";
|
|
11540
|
+
}
|
|
11541
|
+
function platformForPath(filePath) {
|
|
11542
|
+
const normalized = filePath.replace(/\\/g, "/").toLowerCase();
|
|
11543
|
+
const rules = PLATFORMS.filter((p) => !p.isCommand);
|
|
11544
|
+
const inFolder2 = rules.find((p) => p.rulesDirectory && `/${normalized}`.includes(`/${p.rulesDirectory.toLowerCase()}`));
|
|
11545
|
+
if (inFolder2) return inFolder2.id;
|
|
11546
|
+
const file = rules.find((p) => {
|
|
11547
|
+
const own = p.file.toLowerCase();
|
|
11548
|
+
return normalized === own || normalized.endsWith(`/${own}`);
|
|
11549
|
+
});
|
|
11550
|
+
if (file) return file.id;
|
|
11551
|
+
return normalized.endsWith(".mdc") ? "cursor" : void 0;
|
|
11552
|
+
}
|
|
11553
|
+
function convertContent(content, sourcePlatform, targetPlatform) {
|
|
11554
|
+
if (isCommandTarget(targetPlatform)) return content;
|
|
11555
|
+
return adaptRulesContent(content, sourcePlatform, targetPlatform, RULES_DESCRIPTION);
|
|
11556
|
+
}
|
|
11385
11557
|
var SOURCE_FILES = {
|
|
11386
11558
|
"agents.md": "agents",
|
|
11387
11559
|
"claude.md": "claude",
|
|
@@ -11393,8 +11565,33 @@ var SOURCE_FILES = {
|
|
|
11393
11565
|
"copilot-instructions.md": "copilot",
|
|
11394
11566
|
".windsurfrules": "windsurf",
|
|
11395
11567
|
".clinerules": "cline",
|
|
11396
|
-
|
|
11568
|
+
// Real case: auto-detect looks the name up as is, and Linux disks are case-sensitive
|
|
11569
|
+
"CONVENTIONS.md": "aider"
|
|
11397
11570
|
};
|
|
11571
|
+
var sourceFilePlatform = (name) => Object.entries(SOURCE_FILES).find(([file]) => file.toLowerCase() === name.toLowerCase())?.[1];
|
|
11572
|
+
async function detectSourceFile(cwd) {
|
|
11573
|
+
for (const [pattern, platform2] of Object.entries(SOURCE_FILES)) {
|
|
11574
|
+
try {
|
|
11575
|
+
const fullPath = join9(cwd, pattern);
|
|
11576
|
+
await access5(fullPath);
|
|
11577
|
+
return { path: fullPath, platform: platform2 };
|
|
11578
|
+
} catch {
|
|
11579
|
+
}
|
|
11580
|
+
}
|
|
11581
|
+
const uppercaseVariants = ["AGENTS.md", "CLAUDE.md"];
|
|
11582
|
+
for (const variant of uppercaseVariants) {
|
|
11583
|
+
try {
|
|
11584
|
+
const fullPath = join9(cwd, variant);
|
|
11585
|
+
await access5(fullPath);
|
|
11586
|
+
const platform2 = variant.toLowerCase().replace(".md", "");
|
|
11587
|
+
return { path: fullPath, platform: platform2 };
|
|
11588
|
+
} catch {
|
|
11589
|
+
}
|
|
11590
|
+
}
|
|
11591
|
+
return null;
|
|
11592
|
+
}
|
|
11593
|
+
|
|
11594
|
+
// src/commands/convert.ts
|
|
11398
11595
|
var TARGET_FILES = {
|
|
11399
11596
|
...PLATFORM_FILES2,
|
|
11400
11597
|
cursor_legacy: ".cursorrules",
|
|
@@ -11446,27 +11643,6 @@ var COMMAND_DIRS = {
|
|
|
11446
11643
|
...Object.fromEntries(COMMANDS.map((c) => [c.directory, c.id])),
|
|
11447
11644
|
".copilot/prompts": "copilot-prompt"
|
|
11448
11645
|
};
|
|
11449
|
-
async function detectSourceFile(cwd) {
|
|
11450
|
-
for (const [pattern, platform2] of Object.entries(SOURCE_FILES)) {
|
|
11451
|
-
try {
|
|
11452
|
-
const fullPath = join8(cwd, pattern);
|
|
11453
|
-
await access5(fullPath);
|
|
11454
|
-
return { path: fullPath, platform: platform2 };
|
|
11455
|
-
} catch {
|
|
11456
|
-
}
|
|
11457
|
-
}
|
|
11458
|
-
const uppercaseVariants = ["AGENTS.md", "CLAUDE.md"];
|
|
11459
|
-
for (const variant of uppercaseVariants) {
|
|
11460
|
-
try {
|
|
11461
|
-
const fullPath = join8(cwd, variant);
|
|
11462
|
-
await access5(fullPath);
|
|
11463
|
-
const platform2 = variant.toLowerCase().replace(".md", "");
|
|
11464
|
-
return { path: fullPath, platform: platform2 };
|
|
11465
|
-
} catch {
|
|
11466
|
-
}
|
|
11467
|
-
}
|
|
11468
|
-
return null;
|
|
11469
|
-
}
|
|
11470
11646
|
function detectCommandPlatform(sourcePath) {
|
|
11471
11647
|
const normalized = sourcePath.replace(/\\/g, "/").toLowerCase();
|
|
11472
11648
|
for (const [dir, platform2] of Object.entries(COMMAND_DIRS)) {
|
|
@@ -11479,8 +11655,8 @@ function detectCommandPlatform(sourcePath) {
|
|
|
11479
11655
|
async function convertCommand(first, second, options) {
|
|
11480
11656
|
const [source, target] = second === void 0 ? [void 0, first] : [first, second];
|
|
11481
11657
|
console.log();
|
|
11482
|
-
console.log(
|
|
11483
|
-
console.log(
|
|
11658
|
+
console.log(chalk16.cyan.bold(" \u{1F504} LynxPrompt Convert"));
|
|
11659
|
+
console.log(chalk16.gray(" Convert AI IDE configuration between formats"));
|
|
11484
11660
|
console.log();
|
|
11485
11661
|
const cwd = process.cwd();
|
|
11486
11662
|
let sourcePath;
|
|
@@ -11491,27 +11667,26 @@ async function convertCommand(first, second, options) {
|
|
|
11491
11667
|
if (cmdPlatform) {
|
|
11492
11668
|
sourcePlatform = cmdPlatform;
|
|
11493
11669
|
} else {
|
|
11494
|
-
|
|
11495
|
-
sourcePlatform = SOURCE_FILES[sourceBasename] || "unknown";
|
|
11670
|
+
sourcePlatform = platformForPath(source) || sourceFilePlatform(basename2(source)) || "unknown";
|
|
11496
11671
|
}
|
|
11497
11672
|
} else {
|
|
11498
11673
|
const detected = await detectSourceFile(cwd);
|
|
11499
11674
|
if (!detected) {
|
|
11500
|
-
console.log(
|
|
11675
|
+
console.log(chalk16.red(" \u2717 No AI configuration file found in current directory"));
|
|
11501
11676
|
console.log();
|
|
11502
|
-
console.log(
|
|
11677
|
+
console.log(chalk16.gray(" Supported source files:"));
|
|
11503
11678
|
for (const file of Object.keys(SOURCE_FILES)) {
|
|
11504
|
-
console.log(
|
|
11679
|
+
console.log(chalk16.gray(` \u2022 ${file}`));
|
|
11505
11680
|
}
|
|
11506
11681
|
console.log();
|
|
11507
|
-
console.log(
|
|
11682
|
+
console.log(chalk16.gray(" Command directories (for slash commands):"));
|
|
11508
11683
|
for (const dir of Object.keys(COMMAND_DIRS)) {
|
|
11509
|
-
console.log(
|
|
11684
|
+
console.log(chalk16.gray(` \u2022 ${dir}/*.md`));
|
|
11510
11685
|
}
|
|
11511
11686
|
console.log();
|
|
11512
|
-
console.log(
|
|
11513
|
-
console.log(
|
|
11514
|
-
console.log(
|
|
11687
|
+
console.log(chalk16.gray(" Usage: lynxp convert <source> <target>"));
|
|
11688
|
+
console.log(chalk16.gray(" Example: lynxp convert AGENTS.md cursor"));
|
|
11689
|
+
console.log(chalk16.gray(" Example: lynxp convert .cursor/commands/deploy.md claude-command"));
|
|
11515
11690
|
process.exit(1);
|
|
11516
11691
|
}
|
|
11517
11692
|
sourcePath = detected.path;
|
|
@@ -11520,27 +11695,33 @@ async function convertCommand(first, second, options) {
|
|
|
11520
11695
|
const targetLower = target.toLowerCase();
|
|
11521
11696
|
const normalizedTarget = TARGET_FILES[targetLower] ? targetLower : targetLower.replace(/-/g, "_");
|
|
11522
11697
|
if (!TARGET_FILES[normalizedTarget]) {
|
|
11523
|
-
console.log(
|
|
11698
|
+
console.log(chalk16.red(` \u2717 Unknown target format: ${target}`));
|
|
11524
11699
|
console.log();
|
|
11525
|
-
console.log(
|
|
11700
|
+
console.log(chalk16.gray(" Supported target formats:"));
|
|
11526
11701
|
for (const [key, name] of Object.entries(PLATFORM_NAMES)) {
|
|
11527
|
-
|
|
11702
|
+
if (isStructuredPlatform(key)) continue;
|
|
11703
|
+
console.log(chalk16.gray(` \u2022 ${key.padEnd(15)} \u2192 ${name}`));
|
|
11528
11704
|
}
|
|
11529
11705
|
process.exit(1);
|
|
11530
11706
|
}
|
|
11707
|
+
const structured = [sourcePlatform, normalizedTarget].find(isStructuredPlatform);
|
|
11708
|
+
if (structured) {
|
|
11709
|
+
console.log(chalk16.red(` \u2717 ${PLATFORM_NAMES[structured] || structured} uses a JSON/YAML config, not a rules file, so it cannot be converted`));
|
|
11710
|
+
console.log(chalk16.gray(` Run 'lynxp wizard' and pick ${PLATFORM_NAMES[structured] || structured} to generate one`));
|
|
11711
|
+
process.exit(1);
|
|
11712
|
+
}
|
|
11531
11713
|
let sourceContent;
|
|
11532
11714
|
try {
|
|
11533
|
-
sourceContent = await
|
|
11715
|
+
sourceContent = await readFile9(sourcePath, "utf-8");
|
|
11534
11716
|
} catch {
|
|
11535
|
-
console.log(
|
|
11717
|
+
console.log(chalk16.red(` \u2717 Could not read source file: ${sourcePath}`));
|
|
11536
11718
|
process.exit(1);
|
|
11537
11719
|
}
|
|
11538
|
-
console.log(
|
|
11539
|
-
console.log(
|
|
11720
|
+
console.log(chalk16.white(` Source: ${basename2(sourcePath)} (${PLATFORM_NAMES[sourcePlatform] || sourcePlatform})`));
|
|
11721
|
+
console.log(chalk16.white(` Target: ${PLATFORM_NAMES[normalizedTarget]}`));
|
|
11540
11722
|
console.log();
|
|
11541
11723
|
const spinner = ora12("Converting configuration...").start();
|
|
11542
|
-
const
|
|
11543
|
-
const targetContent = generateTargetContent(config2, normalizedTarget);
|
|
11724
|
+
const targetContent = convertContent(sourceContent, sourcePlatform, normalizedTarget);
|
|
11544
11725
|
spinner.stop();
|
|
11545
11726
|
let outputFilename = options.output || TARGET_FILES[normalizedTarget];
|
|
11546
11727
|
const targetCommand = COMMANDS.find((c) => c.id === normalizedTarget);
|
|
@@ -11550,35 +11731,35 @@ async function convertCommand(first, second, options) {
|
|
|
11550
11731
|
}
|
|
11551
11732
|
const outputPath = resolve2(cwd, outputFilename);
|
|
11552
11733
|
try {
|
|
11553
|
-
await
|
|
11734
|
+
await access6(outputPath);
|
|
11554
11735
|
if (!options.force) {
|
|
11555
|
-
console.log(
|
|
11556
|
-
console.log(
|
|
11736
|
+
console.log(chalk16.yellow(` \u26A0\uFE0F File already exists: ${outputFilename}`));
|
|
11737
|
+
console.log(chalk16.gray(" Use --force to overwrite"));
|
|
11557
11738
|
process.exit(1);
|
|
11558
11739
|
}
|
|
11559
11740
|
} catch {
|
|
11560
11741
|
}
|
|
11561
11742
|
try {
|
|
11562
|
-
const { mkdir:
|
|
11563
|
-
const outputDir =
|
|
11743
|
+
const { mkdir: mkdir5 } = await import("fs/promises");
|
|
11744
|
+
const outputDir = dirname5(outputPath);
|
|
11564
11745
|
if (outputDir !== cwd) {
|
|
11565
|
-
await
|
|
11746
|
+
await mkdir5(outputDir, { recursive: true });
|
|
11566
11747
|
}
|
|
11567
|
-
await
|
|
11568
|
-
console.log(
|
|
11748
|
+
await writeFile5(outputPath, targetContent, "utf-8");
|
|
11749
|
+
console.log(chalk16.green(` \u2713 Converted to ${outputFilename}`));
|
|
11569
11750
|
console.log();
|
|
11570
|
-
console.log(
|
|
11571
|
-
console.log(
|
|
11751
|
+
console.log(chalk16.gray(` Lines: ${targetContent.split("\n").length}`));
|
|
11752
|
+
console.log(chalk16.gray(` Size: ${targetContent.length} bytes`));
|
|
11572
11753
|
console.log();
|
|
11573
11754
|
} catch (error) {
|
|
11574
|
-
console.log(
|
|
11755
|
+
console.log(chalk16.red(` \u2717 Could not write output: ${error instanceof Error ? error.message : "unknown error"}`));
|
|
11575
11756
|
process.exit(1);
|
|
11576
11757
|
}
|
|
11577
11758
|
}
|
|
11578
11759
|
|
|
11579
11760
|
// src/commands/merge.ts
|
|
11580
|
-
import
|
|
11581
|
-
import { readFile as
|
|
11761
|
+
import chalk17 from "chalk";
|
|
11762
|
+
import { readFile as readFile10, writeFile as writeFile6, access as access7 } from "fs/promises";
|
|
11582
11763
|
import { basename as basename3, resolve as resolve3 } from "path";
|
|
11583
11764
|
import ora13 from "ora";
|
|
11584
11765
|
import prompts5 from "prompts";
|
|
@@ -11675,43 +11856,43 @@ ${s.content.trim()}`).join("\n\n");
|
|
|
11675
11856
|
// src/commands/merge.ts
|
|
11676
11857
|
async function mergeCommand(files, options) {
|
|
11677
11858
|
console.log();
|
|
11678
|
-
console.log(
|
|
11679
|
-
console.log(
|
|
11859
|
+
console.log(chalk17.cyan.bold(" \u{1F500} LynxPrompt Merge"));
|
|
11860
|
+
console.log(chalk17.gray(" Merge multiple AI IDE configuration files"));
|
|
11680
11861
|
console.log();
|
|
11681
11862
|
if (files.length < 2) {
|
|
11682
|
-
console.log(
|
|
11863
|
+
console.log(chalk17.red(" \u2717 Please provide at least 2 files to merge"));
|
|
11683
11864
|
console.log();
|
|
11684
|
-
console.log(
|
|
11685
|
-
console.log(
|
|
11865
|
+
console.log(chalk17.gray(" Usage: lynxp merge <file1> <file2> [...files]"));
|
|
11866
|
+
console.log(chalk17.gray(" Example: lynxp merge AGENTS.md team-rules.md --output merged.md"));
|
|
11686
11867
|
console.log();
|
|
11687
|
-
console.log(
|
|
11688
|
-
console.log(
|
|
11689
|
-
console.log(
|
|
11690
|
-
console.log(
|
|
11691
|
-
console.log(
|
|
11868
|
+
console.log(chalk17.gray(" Options:"));
|
|
11869
|
+
console.log(chalk17.gray(" --output <file> Output filename (default: merged.md)"));
|
|
11870
|
+
console.log(chalk17.gray(" --strategy <type> Merge strategy: concat, sections, smart (default: smart)"));
|
|
11871
|
+
console.log(chalk17.gray(" --force Overwrite existing output file"));
|
|
11872
|
+
console.log(chalk17.gray(" --interactive Review and select sections to include"));
|
|
11692
11873
|
process.exit(1);
|
|
11693
11874
|
}
|
|
11694
11875
|
const cwd = process.cwd();
|
|
11695
11876
|
const strategy = options.strategy || "smart";
|
|
11696
|
-
console.log(
|
|
11697
|
-
console.log(
|
|
11877
|
+
console.log(chalk17.white(` Files to merge: ${files.length}`));
|
|
11878
|
+
console.log(chalk17.white(` Strategy: ${strategy}`));
|
|
11698
11879
|
console.log();
|
|
11699
11880
|
const allSections = [];
|
|
11700
11881
|
for (const file of files) {
|
|
11701
11882
|
const filePath = resolve3(cwd, file);
|
|
11702
11883
|
try {
|
|
11703
|
-
await
|
|
11884
|
+
await access7(filePath);
|
|
11704
11885
|
} catch {
|
|
11705
|
-
console.log(
|
|
11886
|
+
console.log(chalk17.red(` \u2717 File not found: ${file}`));
|
|
11706
11887
|
process.exit(1);
|
|
11707
11888
|
}
|
|
11708
11889
|
try {
|
|
11709
|
-
const content = await
|
|
11890
|
+
const content = await readFile10(filePath, "utf-8");
|
|
11710
11891
|
const sections = parseMarkdownSections(content, basename3(file));
|
|
11711
11892
|
allSections.push(sections);
|
|
11712
|
-
console.log(
|
|
11893
|
+
console.log(chalk17.gray(` \u2713 Read ${file} (${sections.length} sections)`));
|
|
11713
11894
|
} catch (error) {
|
|
11714
|
-
console.log(
|
|
11895
|
+
console.log(chalk17.red(` \u2717 Could not read ${file}: ${error instanceof Error ? error.message : "unknown"}`));
|
|
11715
11896
|
process.exit(1);
|
|
11716
11897
|
}
|
|
11717
11898
|
}
|
|
@@ -11734,7 +11915,7 @@ async function mergeCommand(files, options) {
|
|
|
11734
11915
|
hint: "- Space to toggle, Enter to confirm"
|
|
11735
11916
|
});
|
|
11736
11917
|
if (!response.sections || response.sections.length === 0) {
|
|
11737
|
-
console.log(
|
|
11918
|
+
console.log(chalk17.yellow(" No sections selected, aborting."));
|
|
11738
11919
|
process.exit(0);
|
|
11739
11920
|
}
|
|
11740
11921
|
const selectedSections = response.sections.map((i) => flatSections[i]);
|
|
@@ -11757,34 +11938,34 @@ ${mergedContent}
|
|
|
11757
11938
|
const outputFilename = options.output || "merged.md";
|
|
11758
11939
|
const outputPath = resolve3(cwd, outputFilename);
|
|
11759
11940
|
try {
|
|
11760
|
-
await
|
|
11941
|
+
await access7(outputPath);
|
|
11761
11942
|
if (!options.force) {
|
|
11762
|
-
console.log(
|
|
11763
|
-
console.log(
|
|
11943
|
+
console.log(chalk17.yellow(` \u26A0\uFE0F File already exists: ${outputFilename}`));
|
|
11944
|
+
console.log(chalk17.gray(" Use --force to overwrite"));
|
|
11764
11945
|
process.exit(1);
|
|
11765
11946
|
}
|
|
11766
11947
|
} catch {
|
|
11767
11948
|
}
|
|
11768
11949
|
try {
|
|
11769
|
-
await
|
|
11770
|
-
console.log(
|
|
11950
|
+
await writeFile6(outputPath, finalContent, "utf-8");
|
|
11951
|
+
console.log(chalk17.green(` \u2713 Merged to ${outputFilename}`));
|
|
11771
11952
|
console.log();
|
|
11772
|
-
console.log(
|
|
11773
|
-
console.log(
|
|
11774
|
-
console.log(
|
|
11953
|
+
console.log(chalk17.gray(` Sources: ${files.length} files`));
|
|
11954
|
+
console.log(chalk17.gray(` Lines: ${finalContent.split("\n").length}`));
|
|
11955
|
+
console.log(chalk17.gray(` Size: ${finalContent.length} bytes`));
|
|
11775
11956
|
console.log();
|
|
11776
11957
|
} catch (error) {
|
|
11777
|
-
console.log(
|
|
11958
|
+
console.log(chalk17.red(` \u2717 Could not write output: ${error instanceof Error ? error.message : "unknown error"}`));
|
|
11778
11959
|
process.exit(1);
|
|
11779
11960
|
}
|
|
11780
11961
|
}
|
|
11781
11962
|
|
|
11782
11963
|
// src/commands/import.ts
|
|
11783
|
-
import
|
|
11964
|
+
import chalk18 from "chalk";
|
|
11784
11965
|
import prompts6 from "prompts";
|
|
11785
11966
|
import ora14 from "ora";
|
|
11786
|
-
import { readFile as
|
|
11787
|
-
import { join as
|
|
11967
|
+
import { readFile as readFile11, access as access8, readdir as readdir3 } from "fs/promises";
|
|
11968
|
+
import { join as join10, relative as relative3, dirname as dirname6, basename as basename4, resolve as resolve4 } from "path";
|
|
11788
11969
|
var CONFIG_FILE_PATTERNS = [
|
|
11789
11970
|
"AGENTS.md",
|
|
11790
11971
|
"CLAUDE.md",
|
|
@@ -11801,7 +11982,7 @@ async function scanDirectory(rootPath, options = {}) {
|
|
|
11801
11982
|
try {
|
|
11802
11983
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
11803
11984
|
for (const entry of entries) {
|
|
11804
|
-
const fullPath =
|
|
11985
|
+
const fullPath = join10(dir, entry.name);
|
|
11805
11986
|
if (entry.isDirectory()) {
|
|
11806
11987
|
const skipDirs = [
|
|
11807
11988
|
"node_modules",
|
|
@@ -11827,13 +12008,13 @@ async function scanDirectory(rootPath, options = {}) {
|
|
|
11827
12008
|
} else if (entry.isFile()) {
|
|
11828
12009
|
if (patterns.includes(entry.name)) {
|
|
11829
12010
|
try {
|
|
11830
|
-
const content = await
|
|
12011
|
+
const content = await readFile11(fullPath, "utf-8");
|
|
11831
12012
|
const relativePath = relative3(rootPath, fullPath);
|
|
11832
12013
|
files.push({
|
|
11833
12014
|
path: fullPath,
|
|
11834
12015
|
relativePath,
|
|
11835
12016
|
depth,
|
|
11836
|
-
name: extractProjectName(content,
|
|
12017
|
+
name: extractProjectName(content, dirname6(fullPath)),
|
|
11837
12018
|
content,
|
|
11838
12019
|
sections: parseMarkdownSections2(content)
|
|
11839
12020
|
});
|
|
@@ -11900,13 +12081,13 @@ function buildHierarchy(files, _rootPath) {
|
|
|
11900
12081
|
const potentialRoots = sorted.filter((f) => f.depth <= 1);
|
|
11901
12082
|
if (potentialRoots.length === 0 && files.length > 0) {
|
|
11902
12083
|
return files.map((f) => ({
|
|
11903
|
-
rootPath:
|
|
12084
|
+
rootPath: dirname6(f.path),
|
|
11904
12085
|
root: f,
|
|
11905
12086
|
children: []
|
|
11906
12087
|
}));
|
|
11907
12088
|
}
|
|
11908
12089
|
for (const root of potentialRoots) {
|
|
11909
|
-
const rootDir =
|
|
12090
|
+
const rootDir = dirname6(root.path);
|
|
11910
12091
|
const children = files.filter((f) => {
|
|
11911
12092
|
if (f === root) return false;
|
|
11912
12093
|
return f.path.startsWith(rootDir + "/") && f.depth > root.depth;
|
|
@@ -11924,7 +12105,7 @@ function buildHierarchy(files, _rootPath) {
|
|
|
11924
12105
|
const orphans = files.filter((f) => !assignedPaths.has(f.path));
|
|
11925
12106
|
for (const orphan of orphans) {
|
|
11926
12107
|
hierarchies.push({
|
|
11927
|
-
rootPath:
|
|
12108
|
+
rootPath: dirname6(orphan.path),
|
|
11928
12109
|
root: orphan,
|
|
11929
12110
|
children: []
|
|
11930
12111
|
});
|
|
@@ -11937,33 +12118,33 @@ function displayResults(result, options) {
|
|
|
11937
12118
|
return;
|
|
11938
12119
|
}
|
|
11939
12120
|
console.log();
|
|
11940
|
-
console.log(
|
|
12121
|
+
console.log(chalk18.cyan.bold(" \u{1F4E5} Import Results"));
|
|
11941
12122
|
console.log();
|
|
11942
12123
|
if (result.totalFound === 0) {
|
|
11943
|
-
console.log(
|
|
11944
|
-
console.log(
|
|
12124
|
+
console.log(chalk18.yellow(" No AGENTS.md or compatible files found."));
|
|
12125
|
+
console.log(chalk18.gray(" Try running from your project root, or use --pattern to specify custom filenames."));
|
|
11945
12126
|
return;
|
|
11946
12127
|
}
|
|
11947
|
-
console.log(
|
|
12128
|
+
console.log(chalk18.green(` Found ${result.totalFound} configuration file(s)`));
|
|
11948
12129
|
console.log();
|
|
11949
12130
|
for (const hierarchy of result.hierarchy) {
|
|
11950
12131
|
const isMonorepo = hierarchy.children.length > 0;
|
|
11951
12132
|
if (hierarchy.root) {
|
|
11952
12133
|
const icon = isMonorepo ? "\u{1F3E2}" : "\u{1F4C4}";
|
|
11953
|
-
const typeLabel = isMonorepo ?
|
|
11954
|
-
console.log(` ${icon} ${
|
|
11955
|
-
console.log(
|
|
12134
|
+
const typeLabel = isMonorepo ? chalk18.magenta("[Monorepo Root]") : chalk18.blue("[Standalone]");
|
|
12135
|
+
console.log(` ${icon} ${chalk18.white.bold(hierarchy.root.name)} ${typeLabel}`);
|
|
12136
|
+
console.log(chalk18.gray(` ${hierarchy.root.relativePath}`));
|
|
11956
12137
|
if (options.verbose && hierarchy.root.sections.length > 0) {
|
|
11957
12138
|
const sectionNames = hierarchy.root.sections.slice(0, 5).map((s) => s.title);
|
|
11958
|
-
console.log(
|
|
12139
|
+
console.log(chalk18.gray(` Sections: ${sectionNames.join(", ")}${hierarchy.root.sections.length > 5 ? "..." : ""}`));
|
|
11959
12140
|
}
|
|
11960
12141
|
}
|
|
11961
12142
|
for (const child of hierarchy.children) {
|
|
11962
|
-
console.log(` \u2514\u2500 \u{1F4C4} ${
|
|
11963
|
-
console.log(
|
|
12143
|
+
console.log(` \u2514\u2500 \u{1F4C4} ${chalk18.white(child.name)}`);
|
|
12144
|
+
console.log(chalk18.gray(` ${child.relativePath}`));
|
|
11964
12145
|
if (options.verbose && child.sections.length > 0) {
|
|
11965
12146
|
const sectionNames = child.sections.slice(0, 3).map((s) => s.title);
|
|
11966
|
-
console.log(
|
|
12147
|
+
console.log(chalk18.gray(` Sections: ${sectionNames.join(", ")}${child.sections.length > 3 ? "..." : ""}`));
|
|
11967
12148
|
}
|
|
11968
12149
|
}
|
|
11969
12150
|
console.log();
|
|
@@ -11971,16 +12152,16 @@ function displayResults(result, options) {
|
|
|
11971
12152
|
const monorepoCount = result.hierarchy.filter((h) => h.children.length > 0).length;
|
|
11972
12153
|
const standaloneCount = result.hierarchy.filter((h) => h.children.length === 0).length;
|
|
11973
12154
|
if (monorepoCount > 0) {
|
|
11974
|
-
console.log(
|
|
12155
|
+
console.log(chalk18.cyan(` \u{1F4CA} ${monorepoCount} monorepo(s) detected with hierarchical configs`));
|
|
11975
12156
|
}
|
|
11976
12157
|
if (standaloneCount > 0) {
|
|
11977
|
-
console.log(
|
|
12158
|
+
console.log(chalk18.cyan(` \u{1F4CA} ${standaloneCount} standalone config(s)`));
|
|
11978
12159
|
}
|
|
11979
12160
|
if (result.errors.length > 0) {
|
|
11980
12161
|
console.log();
|
|
11981
|
-
console.log(
|
|
12162
|
+
console.log(chalk18.yellow(" \u26A0\uFE0F Warnings:"));
|
|
11982
12163
|
for (const error of result.errors) {
|
|
11983
|
-
console.log(
|
|
12164
|
+
console.log(chalk18.gray(` ${error}`));
|
|
11984
12165
|
}
|
|
11985
12166
|
}
|
|
11986
12167
|
}
|
|
@@ -11988,15 +12169,15 @@ async function importCommand(path3 = ".", options) {
|
|
|
11988
12169
|
const json = Boolean(options.json);
|
|
11989
12170
|
if (!json) {
|
|
11990
12171
|
console.log();
|
|
11991
|
-
console.log(
|
|
11992
|
-
console.log(
|
|
12172
|
+
console.log(chalk18.cyan.bold(" \u{1F4E5} LynxPrompt Import"));
|
|
12173
|
+
console.log(chalk18.gray(" Scan and import AGENTS.md files and AI commands from your repository"));
|
|
11993
12174
|
console.log();
|
|
11994
12175
|
}
|
|
11995
12176
|
const rootPath = resolve4(process.cwd(), path3);
|
|
11996
12177
|
try {
|
|
11997
|
-
await
|
|
12178
|
+
await access8(rootPath);
|
|
11998
12179
|
} catch {
|
|
11999
|
-
console.error(
|
|
12180
|
+
console.error(chalk18.red(` \u2717 Path not found: ${rootPath}`));
|
|
12000
12181
|
process.exit(1);
|
|
12001
12182
|
}
|
|
12002
12183
|
const spinner = ora14({ text: "Scanning for configuration files and commands...", isSilent: json }).start();
|
|
@@ -12011,10 +12192,10 @@ async function importCommand(path3 = ".", options) {
|
|
|
12011
12192
|
}
|
|
12012
12193
|
spinner.warn("No configuration files or commands found");
|
|
12013
12194
|
console.log();
|
|
12014
|
-
console.log(
|
|
12015
|
-
console.log(
|
|
12016
|
-
console.log(
|
|
12017
|
-
console.log(
|
|
12195
|
+
console.log(chalk18.gray(" Looking for:"));
|
|
12196
|
+
console.log(chalk18.gray(" \u2022 Rules: AGENTS.md, CLAUDE.md, .cursorrules, .windsurfrules"));
|
|
12197
|
+
console.log(chalk18.gray(" \u2022 Commands: .cursor/commands/*.md, .claude/commands/*.md"));
|
|
12198
|
+
console.log(chalk18.gray(" Try specifying a different path or use --pattern for custom filenames"));
|
|
12018
12199
|
return;
|
|
12019
12200
|
}
|
|
12020
12201
|
if (json) spinner.stop();
|
|
@@ -12031,31 +12212,31 @@ async function importCommand(path3 = ".", options) {
|
|
|
12031
12212
|
if (json) return;
|
|
12032
12213
|
if (commands.length > 0) {
|
|
12033
12214
|
console.log();
|
|
12034
|
-
console.log(
|
|
12215
|
+
console.log(chalk18.cyan.bold(" \u26A1 AI Agent Commands"));
|
|
12035
12216
|
console.log();
|
|
12036
12217
|
const cursorCommands = commands.filter((c) => c.type === "cursor-command");
|
|
12037
12218
|
const claudeCommands = commands.filter((c) => c.type === "claude-command");
|
|
12038
12219
|
if (cursorCommands.length > 0) {
|
|
12039
|
-
console.log(
|
|
12220
|
+
console.log(chalk18.blue(` Cursor Commands (${cursorCommands.length}):`));
|
|
12040
12221
|
for (const cmd of cursorCommands) {
|
|
12041
|
-
console.log(
|
|
12042
|
-
console.log(
|
|
12222
|
+
console.log(chalk18.gray(` \u26A1 ${cmd.name}`));
|
|
12223
|
+
console.log(chalk18.gray(` ${relative3(rootPath, cmd.path)}`));
|
|
12043
12224
|
}
|
|
12044
12225
|
console.log();
|
|
12045
12226
|
}
|
|
12046
12227
|
if (claudeCommands.length > 0) {
|
|
12047
|
-
console.log(
|
|
12228
|
+
console.log(chalk18.yellow(` Claude Commands (${claudeCommands.length}):`));
|
|
12048
12229
|
for (const cmd of claudeCommands) {
|
|
12049
|
-
console.log(
|
|
12050
|
-
console.log(
|
|
12230
|
+
console.log(chalk18.gray(` \u{1F9E0} ${cmd.name}`));
|
|
12231
|
+
console.log(chalk18.gray(` ${relative3(rootPath, cmd.path)}`));
|
|
12051
12232
|
}
|
|
12052
12233
|
console.log();
|
|
12053
12234
|
}
|
|
12054
|
-
console.log(
|
|
12235
|
+
console.log(chalk18.cyan(" \u{1F4A1} Push commands with: lynxp push .cursor/commands/my-command.md"));
|
|
12055
12236
|
}
|
|
12056
12237
|
if (options.dryRun) {
|
|
12057
|
-
console.log(
|
|
12058
|
-
console.log(
|
|
12238
|
+
console.log(chalk18.yellow(" \u{1F4CB} Dry run complete - no changes made"));
|
|
12239
|
+
console.log(chalk18.gray(" Remove --dry-run to proceed with import"));
|
|
12059
12240
|
return;
|
|
12060
12241
|
}
|
|
12061
12242
|
if (!options.json) {
|
|
@@ -12074,15 +12255,15 @@ async function importCommand(path3 = ".", options) {
|
|
|
12074
12255
|
});
|
|
12075
12256
|
switch (action) {
|
|
12076
12257
|
case "convert":
|
|
12077
|
-
console.log(
|
|
12078
|
-
console.log(
|
|
12258
|
+
console.log(chalk18.cyan("\n \u{1F4A1} Use 'lynxp convert <file> agents' to convert individual files"));
|
|
12259
|
+
console.log(chalk18.cyan(" or 'lynxp merge <files...>' to combine multiple configs\n"));
|
|
12079
12260
|
break;
|
|
12080
12261
|
case "save":
|
|
12081
12262
|
await saveHierarchyInfo(rootPath, result);
|
|
12082
12263
|
break;
|
|
12083
12264
|
case "push":
|
|
12084
|
-
console.log(
|
|
12085
|
-
console.log(
|
|
12265
|
+
console.log(chalk18.cyan("\n \u{1F4A1} Use 'lynxp push <file>' to push files individually"));
|
|
12266
|
+
console.log(chalk18.cyan(" Requires login: 'lynxp login'\n"));
|
|
12086
12267
|
break;
|
|
12087
12268
|
default:
|
|
12088
12269
|
break;
|
|
@@ -12090,15 +12271,15 @@ async function importCommand(path3 = ".", options) {
|
|
|
12090
12271
|
}
|
|
12091
12272
|
} catch (error) {
|
|
12092
12273
|
spinner.fail("Import failed");
|
|
12093
|
-
console.error(
|
|
12274
|
+
console.error(chalk18.red(` Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
12094
12275
|
process.exit(1);
|
|
12095
12276
|
}
|
|
12096
12277
|
}
|
|
12097
12278
|
async function saveHierarchyInfo(rootPath, result) {
|
|
12098
|
-
const { writeFile:
|
|
12099
|
-
const configDir =
|
|
12279
|
+
const { writeFile: writeFile7, mkdir: mkdir5 } = await import("fs/promises");
|
|
12280
|
+
const configDir = join10(rootPath, ".lynxprompt");
|
|
12100
12281
|
try {
|
|
12101
|
-
await
|
|
12282
|
+
await mkdir5(configDir, { recursive: true });
|
|
12102
12283
|
const hierarchyInfo = {
|
|
12103
12284
|
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12104
12285
|
rootPath,
|
|
@@ -12114,25 +12295,25 @@ async function saveHierarchyInfo(rootPath, result) {
|
|
|
12114
12295
|
}))
|
|
12115
12296
|
}))
|
|
12116
12297
|
};
|
|
12117
|
-
await
|
|
12118
|
-
|
|
12298
|
+
await writeFile7(
|
|
12299
|
+
join10(configDir, "hierarchy.json"),
|
|
12119
12300
|
JSON.stringify(hierarchyInfo, null, 2),
|
|
12120
12301
|
"utf-8"
|
|
12121
12302
|
);
|
|
12122
|
-
console.log(
|
|
12123
|
-
console.log(
|
|
12303
|
+
console.log(chalk18.green("\n \u2713 Hierarchy saved to .lynxprompt/hierarchy.json"));
|
|
12304
|
+
console.log(chalk18.gray(" The wizard will use this to understand your monorepo structure"));
|
|
12124
12305
|
} catch (error) {
|
|
12125
|
-
console.error(
|
|
12306
|
+
console.error(chalk18.red(` \u2717 Failed to save hierarchy: ${error instanceof Error ? error.message : "Unknown"}`));
|
|
12126
12307
|
}
|
|
12127
12308
|
}
|
|
12128
12309
|
|
|
12129
12310
|
// src/commands/hierarchies.ts
|
|
12130
|
-
import
|
|
12311
|
+
import chalk19 from "chalk";
|
|
12131
12312
|
import ora15 from "ora";
|
|
12132
12313
|
async function hierarchiesCommand(options) {
|
|
12133
12314
|
if (!isAuthenticated()) {
|
|
12134
|
-
console.log(
|
|
12135
|
-
console.log(
|
|
12315
|
+
console.log(chalk19.yellow("You need to be logged in to view hierarchies."));
|
|
12316
|
+
console.log(chalk19.gray("Run 'lynxp login' to authenticate."));
|
|
12136
12317
|
process.exit(1);
|
|
12137
12318
|
}
|
|
12138
12319
|
const spinner = ora15("Fetching hierarchies...").start();
|
|
@@ -12148,32 +12329,32 @@ async function hierarchiesCommand(options) {
|
|
|
12148
12329
|
const { hierarchies, total } = response;
|
|
12149
12330
|
if (hierarchies.length === 0) {
|
|
12150
12331
|
console.log();
|
|
12151
|
-
console.log(
|
|
12332
|
+
console.log(chalk19.yellow("No hierarchies found."));
|
|
12152
12333
|
console.log();
|
|
12153
|
-
console.log(
|
|
12154
|
-
console.log(
|
|
12334
|
+
console.log(chalk19.gray("Hierarchies are created automatically when you push AGENTS.md files from a repository."));
|
|
12335
|
+
console.log(chalk19.gray("Run 'lynxp push AGENTS.md' in a git repository to create a hierarchy."));
|
|
12155
12336
|
console.log();
|
|
12156
12337
|
return;
|
|
12157
12338
|
}
|
|
12158
12339
|
console.log();
|
|
12159
|
-
console.log(
|
|
12340
|
+
console.log(chalk19.cyan(`\u{1F4C1} Your Hierarchies (${total} total)`));
|
|
12160
12341
|
console.log();
|
|
12161
12342
|
for (const hierarchy of hierarchies) {
|
|
12162
|
-
console.log(
|
|
12163
|
-
console.log(
|
|
12343
|
+
console.log(chalk19.bold(` ${hierarchy.name}`));
|
|
12344
|
+
console.log(chalk19.gray(` ID: ${hierarchy.id}`));
|
|
12164
12345
|
if (hierarchy.description) {
|
|
12165
|
-
console.log(
|
|
12346
|
+
console.log(chalk19.gray(` ${hierarchy.description}`));
|
|
12166
12347
|
}
|
|
12167
|
-
console.log(
|
|
12168
|
-
console.log(
|
|
12348
|
+
console.log(chalk19.gray(` Blueprints: ${hierarchy.blueprint_count || 0}`));
|
|
12349
|
+
console.log(chalk19.gray(` Repository: ${hierarchy.repository_root.slice(0, 16)}...`));
|
|
12169
12350
|
console.log();
|
|
12170
12351
|
}
|
|
12171
|
-
console.log(
|
|
12352
|
+
console.log(chalk19.gray("\u2500".repeat(50)));
|
|
12172
12353
|
console.log();
|
|
12173
|
-
console.log(
|
|
12174
|
-
console.log(
|
|
12175
|
-
console.log(
|
|
12176
|
-
console.log(
|
|
12354
|
+
console.log(chalk19.gray("Tips:"));
|
|
12355
|
+
console.log(chalk19.gray(` \u2022 Pull entire hierarchy: lynxp pull <hierarchy_id>`));
|
|
12356
|
+
console.log(chalk19.gray(` \u2022 Preview a hierarchy: lynxp pull <hierarchy_id> --preview`));
|
|
12357
|
+
console.log(chalk19.gray(` \u2022 Push local changes: lynxp push --all`));
|
|
12177
12358
|
console.log();
|
|
12178
12359
|
} catch (error) {
|
|
12179
12360
|
spinner.fail("Failed to fetch hierarchies");
|
|
@@ -12182,46 +12363,46 @@ async function hierarchiesCommand(options) {
|
|
|
12182
12363
|
}
|
|
12183
12364
|
function handleError2(error) {
|
|
12184
12365
|
if (error instanceof ApiRequestError) {
|
|
12185
|
-
console.error(
|
|
12366
|
+
console.error(chalk19.red(`Error: ${error.message}`));
|
|
12186
12367
|
if (error.statusCode === 401) {
|
|
12187
|
-
console.error(
|
|
12368
|
+
console.error(chalk19.gray("Your session may have expired. Run 'lynxp login' to re-authenticate."));
|
|
12188
12369
|
} else if (error.statusCode === 403) {
|
|
12189
|
-
console.error(
|
|
12370
|
+
console.error(chalk19.gray("You don't have permission to access hierarchies."));
|
|
12190
12371
|
}
|
|
12191
12372
|
} else {
|
|
12192
|
-
console.error(
|
|
12373
|
+
console.error(chalk19.red("An unexpected error occurred."));
|
|
12193
12374
|
}
|
|
12194
12375
|
process.exit(1);
|
|
12195
12376
|
}
|
|
12196
12377
|
|
|
12197
12378
|
// src/commands/config.ts
|
|
12198
|
-
import
|
|
12379
|
+
import chalk20 from "chalk";
|
|
12199
12380
|
function printLoginState() {
|
|
12200
12381
|
if (isAuthenticated()) {
|
|
12201
12382
|
const email = getUser()?.email;
|
|
12202
|
-
console.log(
|
|
12383
|
+
console.log(chalk20.gray(` Logged in${email ? ` as ${email}` : ""} on this instance.`));
|
|
12203
12384
|
} else {
|
|
12204
|
-
console.log(
|
|
12385
|
+
console.log(chalk20.gray(" Run 'lynxp login' to authenticate with this instance."));
|
|
12205
12386
|
}
|
|
12206
12387
|
}
|
|
12207
12388
|
async function configCommand(action, valueArg) {
|
|
12208
12389
|
if (!action || action === "show") {
|
|
12209
12390
|
console.log();
|
|
12210
|
-
console.log(
|
|
12391
|
+
console.log(chalk20.cyan("\u2699\uFE0F LynxPrompt CLI Configuration"));
|
|
12211
12392
|
console.log();
|
|
12212
12393
|
console.log(
|
|
12213
|
-
|
|
12394
|
+
chalk20.white(" API URL: ") + chalk20.green(getApiUrl())
|
|
12214
12395
|
);
|
|
12215
12396
|
const loggedIn = isAuthenticated();
|
|
12216
12397
|
console.log(
|
|
12217
|
-
|
|
12398
|
+
chalk20.white(" Login: ") + (loggedIn ? chalk20.green(getUser()?.email ?? "token set") : chalk20.gray("not logged in"))
|
|
12218
12399
|
);
|
|
12219
12400
|
console.log(
|
|
12220
|
-
|
|
12401
|
+
chalk20.white(" Config: ") + chalk20.gray(getConfigPath())
|
|
12221
12402
|
);
|
|
12222
12403
|
if (process.env.LYNXPROMPT_API_URL) {
|
|
12223
12404
|
console.log(
|
|
12224
|
-
|
|
12405
|
+
chalk20.gray(
|
|
12225
12406
|
" (API URL overridden by LYNXPROMPT_API_URL env var)"
|
|
12226
12407
|
)
|
|
12227
12408
|
);
|
|
@@ -12232,20 +12413,20 @@ async function configCommand(action, valueArg) {
|
|
|
12232
12413
|
if (action === "set-url") {
|
|
12233
12414
|
if (!valueArg) {
|
|
12234
12415
|
console.error(
|
|
12235
|
-
|
|
12416
|
+
chalk20.red("Usage: lynxp config set-url <url>")
|
|
12236
12417
|
);
|
|
12237
12418
|
process.exit(1);
|
|
12238
12419
|
}
|
|
12239
12420
|
try {
|
|
12240
12421
|
new URL(valueArg);
|
|
12241
12422
|
} catch {
|
|
12242
|
-
console.error(
|
|
12423
|
+
console.error(chalk20.red(`Invalid URL: ${valueArg}`));
|
|
12243
12424
|
process.exit(1);
|
|
12244
12425
|
}
|
|
12245
12426
|
const clean = valueArg.replace(/\/+$/, "");
|
|
12246
12427
|
setApiUrl(clean);
|
|
12247
12428
|
console.log(
|
|
12248
|
-
|
|
12429
|
+
chalk20.green("\u2713") + ` API URL set to ${chalk20.cyan(clean)}`
|
|
12249
12430
|
);
|
|
12250
12431
|
printLoginState();
|
|
12251
12432
|
return;
|
|
@@ -12253,7 +12434,7 @@ async function configCommand(action, valueArg) {
|
|
|
12253
12434
|
if (action === "reset-url") {
|
|
12254
12435
|
const url = resetApiUrl();
|
|
12255
12436
|
console.log(
|
|
12256
|
-
|
|
12437
|
+
chalk20.green("\u2713") + " API URL reset to " + chalk20.cyan(url)
|
|
12257
12438
|
);
|
|
12258
12439
|
printLoginState();
|
|
12259
12440
|
return;
|
|
@@ -12262,13 +12443,13 @@ async function configCommand(action, valueArg) {
|
|
|
12262
12443
|
console.log(getConfigPath());
|
|
12263
12444
|
return;
|
|
12264
12445
|
}
|
|
12265
|
-
console.error(
|
|
12266
|
-
console.error(
|
|
12446
|
+
console.error(chalk20.red(`Unknown config action: ${action}`));
|
|
12447
|
+
console.error(chalk20.gray("Available: show, set-url <url>, reset-url, path"));
|
|
12267
12448
|
process.exit(1);
|
|
12268
12449
|
}
|
|
12269
12450
|
|
|
12270
12451
|
// src/index.ts
|
|
12271
|
-
var CLI_VERSION2 = "2.1.
|
|
12452
|
+
var CLI_VERSION2 = "2.1.40";
|
|
12272
12453
|
var program = new Command();
|
|
12273
12454
|
program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version(CLI_VERSION2);
|
|
12274
12455
|
program.command("wizard").description("Generate AI IDE configuration (recommended for most users)").option("-n, --name <name>", "Project name").option("-d, --description <description>", "Project description").option("-s, --stack <stack>", "Tech stack (comma-separated)").option("-f, --format <format>", "Output format: agents, cursor, or comma-separated for multiple").option("-p, --platforms <platforms>", "Alias for --format (deprecated)").option("--persona <persona>", "AI persona (fullstack, backend, frontend, devops, data, security)").option("--boundaries <level>", "Boundary preset (conservative, standard, permissive)").option("-y, --yes", "Skip prompts, use defaults (generates AGENTS.md)").option("-o, --output <dir>", "Output directory (default: current directory)").option("--repo-url <url>", "Analyze remote repository URL (GitHub/GitLab supported)").option("--blueprint", "Generate with [[VARIABLE|default]] placeholders for templates").option("--license <type>", "License type (mit, apache-2.0, gpl-3.0, etc.)").option("--ci-cd <platform>", "CI/CD platform (github_actions, gitlab_ci, jenkins, etc.)").option("--project-type <type>", "Project type (work, leisure, opensource, learning)").option("--detect-only", "Only detect project info, don't generate files").option("--load-draft <name>", "Load a saved wizard draft").option("--save-draft <name>", "Save wizard state as a draft (auto-saves at end)").option("--vars <values>", "Fill variables: VAR1=value1,VAR2=value2").action(wizardCommand);
|
|
@@ -12278,7 +12459,7 @@ program.command("convert <source> [target]").description("Convert AI config betw
|
|
|
12278
12459
|
program.command("merge <files...>").description("Merge multiple AI configuration files into one").option("-o, --output <file>", "Output filename (default: merged.md)").option("-s, --strategy <type>", "Merge strategy: concat, sections, smart (default: smart)").option("-f, --force", "Overwrite existing output file").option("-i, --interactive", "Review and select sections to include").action(mergeCommand);
|
|
12279
12460
|
program.command("import [path]").description("Scan and import AGENTS.md files from a repository").option("--dry-run", "Preview what would be imported without changes").option("--no-recursive", "Don't scan subdirectories").option("--depth <n>", "Max directory depth to scan", "10").option("--pattern <file>", "Custom config filename to look for").option("-v, --verbose", "Show detailed section information").option("-j, --json", "Output as JSON").action(importCommand);
|
|
12280
12461
|
program.command("status").description("Show current AI configuration and tracked blueprints").action(statusCommand);
|
|
12281
|
-
program.command("pull <id>").description("Download a blueprint (bp_xxx) or entire hierarchy (ha_xxx)").option("-o, --output <path>", "Output directory", ".").option("-y, --yes", "Overwrite existing files without prompting").option("--preview", "Preview content without downloading").option("--no-track", "Don't track the blueprint for future syncs").action(pullCommand);
|
|
12462
|
+
program.command("pull <id>").description("Download a blueprint (bp_xxx) or entire hierarchy (ha_xxx)").option("-o, --output <path>", "Output directory", ".").option("-y, --yes", "Overwrite existing files without prompting").option("--preview", "Preview content without downloading").option("--no-track", "Don't track the blueprint for future syncs").option("--vars <values>", "Fill [[VARIABLES]]: VAR1=value1,VAR2=value2 (others get their defaults)").action(pullCommand);
|
|
12282
12463
|
program.command("search <query>").description("Search public blueprints in the marketplace").option("-l, --limit <number>", "Number of results", "20").action(searchCommand);
|
|
12283
12464
|
program.command("list").description("List your blueprints").option("-l, --limit <number>", "Number of results", "20").option("-v, --visibility <visibility>", "Filter: PRIVATE, TEAM, PUBLIC, or all").action(listCommand);
|
|
12284
12465
|
program.command("push [file]").description("Push local file to LynxPrompt cloud as a blueprint").option("-a, --all", "Scan recursively and push ALL AI config files (40+ types)").option("-n, --name <name>", "Blueprint name").option("-d, --description <desc>", "Blueprint description").option("-v, --visibility <vis>", "Visibility: PRIVATE, TEAM, or PUBLIC (default: PRIVATE)").option("-t, --tags <tags>", "Tags (comma-separated)").option("-y, --yes", "Skip prompts").option("-f, --force", "Force push (overwrite remote changes)").option("--allow-sensitive", "Publish public content even if it looks like it holds secrets").action(pushCommand);
|
|
@@ -12286,6 +12467,7 @@ program.command("hierarchies").description("List your blueprint hierarchies (mon
|
|
|
12286
12467
|
program.command("link [file] [blueprint-id]").description("Link a local file to a cloud blueprint for tracking").option("--list", "List all tracked blueprints").action(linkCommand);
|
|
12287
12468
|
program.command("unlink [file]").description("Disconnect a local file from its cloud blueprint").action(unlinkCommand);
|
|
12288
12469
|
program.command("diff [file-or-id]").description("Compare tracked files with their cloud blueprints").option("--local", "Compare .lynxprompt/rules/ with exported files").action(diffCommand);
|
|
12470
|
+
program.command("sync [file]").description("Pull cloud changes and push local ones for tracked files; conflicts go to <file>.remote").option("--dry-run", "Show what would change without writing or pushing").action(syncCommand);
|
|
12289
12471
|
program.command("login").description("Authenticate with LynxPrompt (opens browser)").action(loginCommand);
|
|
12290
12472
|
program.command("logout").description("Log out and remove stored credentials").action(logoutCommand);
|
|
12291
12473
|
program.command("whoami").description("Show current authenticated user").action(whoamiCommand);
|
|
@@ -12293,51 +12475,53 @@ program.command("config [action] [value]").description("View or change CLI setti
|
|
|
12293
12475
|
program.addHelpText(
|
|
12294
12476
|
"beforeAll",
|
|
12295
12477
|
`
|
|
12296
|
-
${
|
|
12297
|
-
${
|
|
12478
|
+
${chalk21.cyan("\u{1F431} LynxPrompt CLI")} ${chalk21.gray("(also available as: lynxp)")}
|
|
12479
|
+
${chalk21.gray("Generate AI IDE configuration files from your terminal")}
|
|
12298
12480
|
`
|
|
12299
12481
|
);
|
|
12300
12482
|
program.addHelpText(
|
|
12301
12483
|
"after",
|
|
12302
12484
|
`
|
|
12303
|
-
${
|
|
12304
|
-
${
|
|
12305
|
-
${
|
|
12306
|
-
${
|
|
12307
|
-
${
|
|
12308
|
-
|
|
12309
|
-
${
|
|
12310
|
-
${
|
|
12311
|
-
${
|
|
12312
|
-
${
|
|
12313
|
-
${
|
|
12314
|
-
${
|
|
12315
|
-
${
|
|
12316
|
-
|
|
12317
|
-
${
|
|
12318
|
-
${
|
|
12319
|
-
${
|
|
12320
|
-
${
|
|
12321
|
-
${
|
|
12322
|
-
${
|
|
12323
|
-
${
|
|
12324
|
-
${
|
|
12325
|
-
|
|
12326
|
-
${
|
|
12327
|
-
${
|
|
12328
|
-
${
|
|
12329
|
-
${
|
|
12330
|
-
|
|
12331
|
-
${
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
${
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
${
|
|
12338
|
-
${
|
|
12339
|
-
|
|
12340
|
-
${
|
|
12485
|
+
${chalk21.cyan("Quick Start:")}
|
|
12486
|
+
${chalk21.white("$ lynxp wizard")} ${chalk21.gray("Generate config interactively")}
|
|
12487
|
+
${chalk21.white("$ lynxp wizard -y")} ${chalk21.gray("Generate AGENTS.md with defaults")}
|
|
12488
|
+
${chalk21.white("$ lynxp wizard -f cursor")} ${chalk21.gray("Generate .cursor/rules/")}
|
|
12489
|
+
${chalk21.white("$ lynxp wizard --blueprint")} ${chalk21.gray("Generate with [[VAR|default]] placeholders")}
|
|
12490
|
+
|
|
12491
|
+
${chalk21.cyan("Analysis & Tools:")}
|
|
12492
|
+
${chalk21.white("$ lynxp analyze")} ${chalk21.gray("Analyze project tech stack")}
|
|
12493
|
+
${chalk21.white("$ lynxp analyze -r <url>")} ${chalk21.gray("Analyze remote repository")}
|
|
12494
|
+
${chalk21.white("$ lynxp import")} ${chalk21.gray("Scan repo for AGENTS.md files")}
|
|
12495
|
+
${chalk21.white("$ lynxp import --dry-run")} ${chalk21.gray("Preview monorepo hierarchy")}
|
|
12496
|
+
${chalk21.white("$ lynxp convert AGENTS.md cursor")} ${chalk21.gray("Convert to Cursor format")}
|
|
12497
|
+
${chalk21.white("$ lynxp merge a.md b.md -o out.md")} ${chalk21.gray("Merge multiple configs")}
|
|
12498
|
+
|
|
12499
|
+
${chalk21.cyan("Marketplace:")}
|
|
12500
|
+
${chalk21.white("$ lynxp search nextjs")} ${chalk21.gray("Search blueprints")}
|
|
12501
|
+
${chalk21.white("$ lynxp pull bp_abc123")} ${chalk21.gray("Download and track a blueprint")}
|
|
12502
|
+
${chalk21.white("$ lynxp pull ha_xyz789")} ${chalk21.gray("Download entire hierarchy")}
|
|
12503
|
+
${chalk21.white("$ lynxp push")} ${chalk21.gray("Push local file to cloud")}
|
|
12504
|
+
${chalk21.white("$ lynxp push --all")} ${chalk21.gray("Push ALL config files recursively")}
|
|
12505
|
+
${chalk21.white("$ lynxp hierarchies")} ${chalk21.gray("List your hierarchies")}
|
|
12506
|
+
${chalk21.white("$ lynxp link --list")} ${chalk21.gray("Show tracked blueprints")}
|
|
12507
|
+
|
|
12508
|
+
${chalk21.cyan("Blueprint Tracking:")}
|
|
12509
|
+
${chalk21.white("$ lynxp link AGENTS.md bp_xyz")} ${chalk21.gray("Link existing file to blueprint")}
|
|
12510
|
+
${chalk21.white("$ lynxp unlink AGENTS.md")} ${chalk21.gray("Disconnect from cloud")}
|
|
12511
|
+
${chalk21.white("$ lynxp diff bp_abc123")} ${chalk21.gray("Show changes vs cloud version")}
|
|
12512
|
+
${chalk21.white("$ lynxp sync")} ${chalk21.gray("Pull cloud changes, push local ones")}
|
|
12513
|
+
${chalk21.white("$ lynxp pull bp_x --vars A=1")} ${chalk21.gray("Download with [[A]] filled in")}
|
|
12514
|
+
|
|
12515
|
+
${chalk21.cyan("CI/CD:")}
|
|
12516
|
+
${chalk21.white("$ lynxp check --ci")} ${chalk21.gray("Validate config (exit code)")}
|
|
12517
|
+
|
|
12518
|
+
${chalk21.cyan("Configuration:")}
|
|
12519
|
+
${chalk21.white("$ lynxp config")} ${chalk21.gray("Show current settings")}
|
|
12520
|
+
${chalk21.white("$ lynxp config set-url <url>")} ${chalk21.gray("Point CLI to a self-hosted instance")}
|
|
12521
|
+
${chalk21.white("$ lynxp config reset-url")} ${chalk21.gray("Reset to https://lynxprompt.com")}
|
|
12522
|
+
${chalk21.white("$ lynxp config path")} ${chalk21.gray("Show config file location")}
|
|
12523
|
+
|
|
12524
|
+
${chalk21.gray("Docs: https://lynxprompt.com/docs/cli")}
|
|
12341
12525
|
`
|
|
12342
12526
|
);
|
|
12343
12527
|
program.parse();
|