lynxprompt 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +316 -924
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk16 from "chalk";
|
|
6
|
+
import { createRequire } from "module";
|
|
6
7
|
|
|
7
8
|
// src/commands/login.ts
|
|
8
9
|
import chalk from "chalk";
|
|
@@ -5340,8 +5341,8 @@ async function statusCommand() {
|
|
|
5340
5341
|
if (existsSync2(configPath)) {
|
|
5341
5342
|
try {
|
|
5342
5343
|
const content = await readFile5(configPath, "utf-8");
|
|
5343
|
-
const { parse:
|
|
5344
|
-
const config2 =
|
|
5344
|
+
const { parse: parse3 } = await import("yaml");
|
|
5345
|
+
const config2 = parse3(content);
|
|
5345
5346
|
if (config2?.exporters?.length > 0) {
|
|
5346
5347
|
console.log(chalk9.gray(` Exporters: ${config2.exporters.join(", ")}`));
|
|
5347
5348
|
}
|
|
@@ -5458,623 +5459,13 @@ function formatBytes(bytes) {
|
|
|
5458
5459
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
5459
5460
|
}
|
|
5460
5461
|
|
|
5461
|
-
// src/commands/agents.ts
|
|
5462
|
-
import chalk10 from "chalk";
|
|
5463
|
-
import prompts4 from "prompts";
|
|
5464
|
-
import { readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
5465
|
-
import { join as join7 } from "path";
|
|
5466
|
-
import { existsSync as existsSync4 } from "fs";
|
|
5467
|
-
import * as yaml2 from "yaml";
|
|
5468
|
-
|
|
5469
|
-
// src/utils/agents.ts
|
|
5470
|
-
var AGENTS = [
|
|
5471
|
-
// === POPULAR AGENTS ===
|
|
5472
|
-
{
|
|
5473
|
-
id: "cursor",
|
|
5474
|
-
name: "Cursor",
|
|
5475
|
-
description: "AI-powered code editor with .cursor/rules/ support",
|
|
5476
|
-
patterns: [".cursor/rules/"],
|
|
5477
|
-
output: ".cursor/rules/",
|
|
5478
|
-
format: "mdc",
|
|
5479
|
-
category: "popular",
|
|
5480
|
-
popular: true
|
|
5481
|
-
},
|
|
5482
|
-
{
|
|
5483
|
-
id: "agents",
|
|
5484
|
-
name: "AGENTS.md",
|
|
5485
|
-
description: "Universal format for Claude Code, GitHub Copilot, Aider, and others",
|
|
5486
|
-
patterns: ["AGENTS.md"],
|
|
5487
|
-
output: "AGENTS.md",
|
|
5488
|
-
format: "markdown",
|
|
5489
|
-
category: "popular",
|
|
5490
|
-
popular: true
|
|
5491
|
-
},
|
|
5492
|
-
{
|
|
5493
|
-
id: "claude",
|
|
5494
|
-
name: "Claude Code",
|
|
5495
|
-
description: "Anthropic's Claude with CLAUDE.md support",
|
|
5496
|
-
patterns: ["CLAUDE.md"],
|
|
5497
|
-
output: "CLAUDE.md",
|
|
5498
|
-
format: "markdown",
|
|
5499
|
-
category: "popular",
|
|
5500
|
-
popular: true
|
|
5501
|
-
},
|
|
5502
|
-
{
|
|
5503
|
-
id: "copilot",
|
|
5504
|
-
name: "GitHub Copilot",
|
|
5505
|
-
description: "GitHub's AI pair programmer",
|
|
5506
|
-
patterns: [".github/copilot-instructions.md"],
|
|
5507
|
-
output: ".github/copilot-instructions.md",
|
|
5508
|
-
format: "markdown",
|
|
5509
|
-
category: "popular",
|
|
5510
|
-
popular: true
|
|
5511
|
-
},
|
|
5512
|
-
{
|
|
5513
|
-
id: "windsurf",
|
|
5514
|
-
name: "Windsurf",
|
|
5515
|
-
description: "Codeium's AI IDE with .windsurfrules support",
|
|
5516
|
-
patterns: [".windsurfrules", ".windsurf/rules/"],
|
|
5517
|
-
output: ".windsurfrules",
|
|
5518
|
-
format: "text",
|
|
5519
|
-
category: "popular",
|
|
5520
|
-
popular: true
|
|
5521
|
-
},
|
|
5522
|
-
// === MARKDOWN FORMAT AGENTS ===
|
|
5523
|
-
{
|
|
5524
|
-
id: "antigravity",
|
|
5525
|
-
name: "Antigravity",
|
|
5526
|
-
description: "Google's AI-powered IDE with GEMINI.md support",
|
|
5527
|
-
patterns: ["GEMINI.md"],
|
|
5528
|
-
output: "GEMINI.md",
|
|
5529
|
-
format: "markdown",
|
|
5530
|
-
category: "popular",
|
|
5531
|
-
popular: true
|
|
5532
|
-
},
|
|
5533
|
-
{
|
|
5534
|
-
id: "gemini",
|
|
5535
|
-
name: "Gemini CLI",
|
|
5536
|
-
description: "Google's Gemini CLI tool",
|
|
5537
|
-
patterns: ["GEMINI.md"],
|
|
5538
|
-
output: "GEMINI.md",
|
|
5539
|
-
format: "markdown",
|
|
5540
|
-
category: "markdown"
|
|
5541
|
-
},
|
|
5542
|
-
{
|
|
5543
|
-
id: "warp",
|
|
5544
|
-
name: "Warp AI",
|
|
5545
|
-
description: "Warp terminal's AI assistant",
|
|
5546
|
-
patterns: ["WARP.md"],
|
|
5547
|
-
output: "WARP.md",
|
|
5548
|
-
format: "markdown",
|
|
5549
|
-
category: "markdown"
|
|
5550
|
-
},
|
|
5551
|
-
{
|
|
5552
|
-
id: "zed",
|
|
5553
|
-
name: "Zed",
|
|
5554
|
-
description: "High-performance code editor with AI features",
|
|
5555
|
-
patterns: [".zed/instructions.md", "ZED.md"],
|
|
5556
|
-
output: ".zed/instructions.md",
|
|
5557
|
-
format: "markdown",
|
|
5558
|
-
category: "markdown"
|
|
5559
|
-
},
|
|
5560
|
-
{
|
|
5561
|
-
id: "crush",
|
|
5562
|
-
name: "Crush",
|
|
5563
|
-
description: "AI coding assistant",
|
|
5564
|
-
patterns: ["CRUSH.md"],
|
|
5565
|
-
output: "CRUSH.md",
|
|
5566
|
-
format: "markdown",
|
|
5567
|
-
category: "markdown"
|
|
5568
|
-
},
|
|
5569
|
-
{
|
|
5570
|
-
id: "junie",
|
|
5571
|
-
name: "Junie",
|
|
5572
|
-
description: "JetBrains' AI coding assistant",
|
|
5573
|
-
patterns: [".junie/guidelines.md"],
|
|
5574
|
-
output: ".junie/guidelines.md",
|
|
5575
|
-
format: "markdown",
|
|
5576
|
-
category: "markdown"
|
|
5577
|
-
},
|
|
5578
|
-
{
|
|
5579
|
-
id: "openhands",
|
|
5580
|
-
name: "OpenHands",
|
|
5581
|
-
description: "Open-source AI coding agent",
|
|
5582
|
-
patterns: [".openhands/microagents/repo.md"],
|
|
5583
|
-
output: ".openhands/microagents/repo.md",
|
|
5584
|
-
format: "markdown",
|
|
5585
|
-
category: "markdown"
|
|
5586
|
-
},
|
|
5587
|
-
// === PLAIN TEXT FORMAT ===
|
|
5588
|
-
{
|
|
5589
|
-
id: "cline",
|
|
5590
|
-
name: "Cline",
|
|
5591
|
-
description: "VS Code AI assistant extension",
|
|
5592
|
-
patterns: [".clinerules"],
|
|
5593
|
-
output: ".clinerules",
|
|
5594
|
-
format: "text",
|
|
5595
|
-
category: "config"
|
|
5596
|
-
},
|
|
5597
|
-
{
|
|
5598
|
-
id: "goose",
|
|
5599
|
-
name: "Goose",
|
|
5600
|
-
description: "Block's AI coding assistant",
|
|
5601
|
-
patterns: [".goosehints"],
|
|
5602
|
-
output: ".goosehints",
|
|
5603
|
-
format: "text",
|
|
5604
|
-
category: "config"
|
|
5605
|
-
},
|
|
5606
|
-
{
|
|
5607
|
-
id: "aider",
|
|
5608
|
-
name: "Aider",
|
|
5609
|
-
description: "AI pair programming in your terminal",
|
|
5610
|
-
patterns: [".aider.conf.yml", "AIDER.md"],
|
|
5611
|
-
output: "AIDER.md",
|
|
5612
|
-
format: "markdown",
|
|
5613
|
-
category: "config"
|
|
5614
|
-
},
|
|
5615
|
-
// === DIRECTORY-BASED AGENTS ===
|
|
5616
|
-
{
|
|
5617
|
-
id: "amazonq",
|
|
5618
|
-
name: "Amazon Q",
|
|
5619
|
-
description: "AWS's AI coding assistant",
|
|
5620
|
-
patterns: [".amazonq/rules/"],
|
|
5621
|
-
output: ".amazonq/rules/",
|
|
5622
|
-
format: "mdc",
|
|
5623
|
-
category: "directory"
|
|
5624
|
-
},
|
|
5625
|
-
{
|
|
5626
|
-
id: "augmentcode",
|
|
5627
|
-
name: "Augment Code",
|
|
5628
|
-
description: "AI code augmentation tool",
|
|
5629
|
-
patterns: [".augment/rules/"],
|
|
5630
|
-
output: ".augment/rules/",
|
|
5631
|
-
format: "mdc",
|
|
5632
|
-
category: "directory"
|
|
5633
|
-
},
|
|
5634
|
-
{
|
|
5635
|
-
id: "kilocode",
|
|
5636
|
-
name: "Kilocode",
|
|
5637
|
-
description: "AI-powered code generation",
|
|
5638
|
-
patterns: [".kilocode/rules/"],
|
|
5639
|
-
output: ".kilocode/rules/",
|
|
5640
|
-
format: "mdc",
|
|
5641
|
-
category: "directory"
|
|
5642
|
-
},
|
|
5643
|
-
{
|
|
5644
|
-
id: "kiro",
|
|
5645
|
-
name: "Kiro",
|
|
5646
|
-
description: "AWS's spec-driven AI coding agent",
|
|
5647
|
-
patterns: [".kiro/steering/"],
|
|
5648
|
-
output: ".kiro/steering/",
|
|
5649
|
-
format: "mdc",
|
|
5650
|
-
category: "directory"
|
|
5651
|
-
},
|
|
5652
|
-
{
|
|
5653
|
-
id: "trae-ai",
|
|
5654
|
-
name: "Trae AI",
|
|
5655
|
-
description: "ByteDance's AI coding assistant",
|
|
5656
|
-
patterns: [".trae/rules/"],
|
|
5657
|
-
output: ".trae/rules/",
|
|
5658
|
-
format: "mdc",
|
|
5659
|
-
category: "directory"
|
|
5660
|
-
},
|
|
5661
|
-
{
|
|
5662
|
-
id: "firebase-studio",
|
|
5663
|
-
name: "Firebase Studio",
|
|
5664
|
-
description: "Google's Firebase development environment",
|
|
5665
|
-
patterns: [".idx/"],
|
|
5666
|
-
output: ".idx/",
|
|
5667
|
-
format: "mdc",
|
|
5668
|
-
category: "directory"
|
|
5669
|
-
},
|
|
5670
|
-
{
|
|
5671
|
-
id: "roocode",
|
|
5672
|
-
name: "Roo Code",
|
|
5673
|
-
description: "AI coding assistant for VS Code",
|
|
5674
|
-
patterns: [".roo/rules/"],
|
|
5675
|
-
output: ".roo/rules/",
|
|
5676
|
-
format: "mdc",
|
|
5677
|
-
category: "directory"
|
|
5678
|
-
},
|
|
5679
|
-
// === JSON/CONFIG FORMAT ===
|
|
5680
|
-
{
|
|
5681
|
-
id: "firebender",
|
|
5682
|
-
name: "Firebender",
|
|
5683
|
-
description: "AI code transformation tool",
|
|
5684
|
-
patterns: ["firebender.json"],
|
|
5685
|
-
output: "firebender.json",
|
|
5686
|
-
format: "json",
|
|
5687
|
-
category: "config"
|
|
5688
|
-
},
|
|
5689
|
-
{
|
|
5690
|
-
id: "opencode",
|
|
5691
|
-
name: "Open Code",
|
|
5692
|
-
description: "Open-source AI coding tool",
|
|
5693
|
-
patterns: ["opencode.json"],
|
|
5694
|
-
output: "opencode.json",
|
|
5695
|
-
format: "json",
|
|
5696
|
-
category: "config"
|
|
5697
|
-
},
|
|
5698
|
-
// === MCP (Model Context Protocol) AGENTS ===
|
|
5699
|
-
{
|
|
5700
|
-
id: "vscode-mcp",
|
|
5701
|
-
name: "VS Code MCP",
|
|
5702
|
-
description: "VS Code with Model Context Protocol",
|
|
5703
|
-
patterns: [".vscode/mcp.json"],
|
|
5704
|
-
output: ".vscode/mcp.json",
|
|
5705
|
-
format: "json",
|
|
5706
|
-
category: "mcp"
|
|
5707
|
-
},
|
|
5708
|
-
{
|
|
5709
|
-
id: "cursor-mcp",
|
|
5710
|
-
name: "Cursor MCP",
|
|
5711
|
-
description: "Cursor with Model Context Protocol",
|
|
5712
|
-
patterns: [".cursor/mcp.json"],
|
|
5713
|
-
output: ".cursor/mcp.json",
|
|
5714
|
-
format: "json",
|
|
5715
|
-
category: "mcp"
|
|
5716
|
-
},
|
|
5717
|
-
{
|
|
5718
|
-
id: "root-mcp",
|
|
5719
|
-
name: "Root MCP",
|
|
5720
|
-
description: "Root-level MCP config for Claude Code, Aider",
|
|
5721
|
-
patterns: [".mcp.json"],
|
|
5722
|
-
output: ".mcp.json",
|
|
5723
|
-
format: "json",
|
|
5724
|
-
category: "mcp"
|
|
5725
|
-
},
|
|
5726
|
-
{
|
|
5727
|
-
id: "windsurf-mcp",
|
|
5728
|
-
name: "Windsurf MCP",
|
|
5729
|
-
description: "Windsurf with Model Context Protocol",
|
|
5730
|
-
patterns: [".windsurf/mcp_config.json"],
|
|
5731
|
-
output: ".windsurf/mcp_config.json",
|
|
5732
|
-
format: "json",
|
|
5733
|
-
category: "mcp"
|
|
5734
|
-
}
|
|
5735
|
-
];
|
|
5736
|
-
function getAgent(id) {
|
|
5737
|
-
return AGENTS.find((a) => a.id === id);
|
|
5738
|
-
}
|
|
5739
|
-
function getPopularAgents() {
|
|
5740
|
-
return AGENTS.filter((a) => a.popular);
|
|
5741
|
-
}
|
|
5742
|
-
function getAgentDisplayName(id) {
|
|
5743
|
-
const agent = getAgent(id);
|
|
5744
|
-
return agent?.name ?? id;
|
|
5745
|
-
}
|
|
5746
|
-
|
|
5747
|
-
// src/utils/agent-detector.ts
|
|
5748
|
-
import { existsSync as existsSync3, readdirSync, readFileSync, statSync } from "fs";
|
|
5749
|
-
import { join as join6 } from "path";
|
|
5750
|
-
function detectAgents(cwd = process.cwd()) {
|
|
5751
|
-
const detected = [];
|
|
5752
|
-
for (const agent of AGENTS) {
|
|
5753
|
-
const result = detectAgent(cwd, agent);
|
|
5754
|
-
if (result) {
|
|
5755
|
-
detected.push(result);
|
|
5756
|
-
}
|
|
5757
|
-
}
|
|
5758
|
-
const popularDetected = detected.filter((d) => d.agent.popular);
|
|
5759
|
-
const importable = detected.filter((d) => d.hasContent);
|
|
5760
|
-
let summary;
|
|
5761
|
-
if (detected.length === 0) {
|
|
5762
|
-
summary = "No AI agent configuration files detected";
|
|
5763
|
-
} else if (detected.length === 1) {
|
|
5764
|
-
summary = `Found ${getAgentDisplayName(detected[0].agent.id)} configuration`;
|
|
5765
|
-
} else {
|
|
5766
|
-
const names = detected.slice(0, 3).map((d) => d.agent.name);
|
|
5767
|
-
const more = detected.length > 3 ? ` +${detected.length - 3} more` : "";
|
|
5768
|
-
summary = `Found ${detected.length} agents: ${names.join(", ")}${more}`;
|
|
5769
|
-
}
|
|
5770
|
-
return { detected, popularDetected, importable, summary };
|
|
5771
|
-
}
|
|
5772
|
-
function detectAgent(cwd, agent) {
|
|
5773
|
-
const files = [];
|
|
5774
|
-
let hasContent = false;
|
|
5775
|
-
let ruleCount = 0;
|
|
5776
|
-
for (const pattern of agent.patterns) {
|
|
5777
|
-
const fullPath = join6(cwd, pattern);
|
|
5778
|
-
if (!existsSync3(fullPath)) {
|
|
5779
|
-
continue;
|
|
5780
|
-
}
|
|
5781
|
-
const stats = statSync(fullPath);
|
|
5782
|
-
if (stats.isDirectory()) {
|
|
5783
|
-
const dirFiles = scanDirectory(fullPath, agent.format);
|
|
5784
|
-
if (dirFiles.length > 0) {
|
|
5785
|
-
files.push(pattern);
|
|
5786
|
-
hasContent = true;
|
|
5787
|
-
ruleCount += dirFiles.reduce((sum, f) => sum + f.sections, 0);
|
|
5788
|
-
}
|
|
5789
|
-
} else if (stats.isFile()) {
|
|
5790
|
-
files.push(pattern);
|
|
5791
|
-
const content = safeReadFile(fullPath);
|
|
5792
|
-
if (content && content.trim().length > 0) {
|
|
5793
|
-
hasContent = true;
|
|
5794
|
-
ruleCount += countSections(content);
|
|
5795
|
-
}
|
|
5796
|
-
}
|
|
5797
|
-
}
|
|
5798
|
-
if (files.length === 0) {
|
|
5799
|
-
return null;
|
|
5800
|
-
}
|
|
5801
|
-
return { agent, files, hasContent, ruleCount };
|
|
5802
|
-
}
|
|
5803
|
-
function scanDirectory(dirPath, format) {
|
|
5804
|
-
const results = [];
|
|
5805
|
-
try {
|
|
5806
|
-
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
5807
|
-
for (const entry of entries) {
|
|
5808
|
-
if (!entry.isFile()) continue;
|
|
5809
|
-
const name = entry.name.toLowerCase();
|
|
5810
|
-
const isRuleFile = format === "mdc" && name.endsWith(".mdc") || format === "markdown" && name.endsWith(".md") || format === "json" && name.endsWith(".json") || format === "yaml" && (name.endsWith(".yml") || name.endsWith(".yaml"));
|
|
5811
|
-
if (!isRuleFile) continue;
|
|
5812
|
-
const filePath = join6(dirPath, entry.name);
|
|
5813
|
-
const content = safeReadFile(filePath);
|
|
5814
|
-
if (content && content.trim().length > 0) {
|
|
5815
|
-
results.push({
|
|
5816
|
-
path: filePath,
|
|
5817
|
-
sections: countSections(content)
|
|
5818
|
-
});
|
|
5819
|
-
}
|
|
5820
|
-
}
|
|
5821
|
-
} catch {
|
|
5822
|
-
}
|
|
5823
|
-
return results;
|
|
5824
|
-
}
|
|
5825
|
-
function countSections(content) {
|
|
5826
|
-
const headings = content.match(/^#{1,6}\s+.+$/gm);
|
|
5827
|
-
return headings ? headings.length : content.trim().length > 0 ? 1 : 0;
|
|
5828
|
-
}
|
|
5829
|
-
function safeReadFile(path2) {
|
|
5830
|
-
try {
|
|
5831
|
-
return readFileSync(path2, "utf-8");
|
|
5832
|
-
} catch {
|
|
5833
|
-
return null;
|
|
5834
|
-
}
|
|
5835
|
-
}
|
|
5836
|
-
function formatDetectionResults(result) {
|
|
5837
|
-
if (result.detected.length === 0) {
|
|
5838
|
-
return "No AI agent configuration files found.\n\nRun 'lynxp wizard' to create your first configuration.";
|
|
5839
|
-
}
|
|
5840
|
-
const lines = [
|
|
5841
|
-
`Found ${result.detected.length} AI agent${result.detected.length === 1 ? "" : "s"}:`,
|
|
5842
|
-
""
|
|
5843
|
-
];
|
|
5844
|
-
for (const detected of result.detected) {
|
|
5845
|
-
const icon = detected.hasContent ? "\u2713" : "\u25CB";
|
|
5846
|
-
const rules = detected.ruleCount > 0 ? ` (${detected.ruleCount} sections)` : "";
|
|
5847
|
-
lines.push(` ${icon} ${detected.agent.name}${rules}`);
|
|
5848
|
-
for (const file of detected.files) {
|
|
5849
|
-
lines.push(` \u2514\u2500 ${file}`);
|
|
5850
|
-
}
|
|
5851
|
-
}
|
|
5852
|
-
if (result.importable.length > 0) {
|
|
5853
|
-
lines.push("");
|
|
5854
|
-
lines.push(`${result.importable.length} can be imported into LynxPrompt.`);
|
|
5855
|
-
}
|
|
5856
|
-
return lines.join("\n");
|
|
5857
|
-
}
|
|
5858
|
-
|
|
5859
|
-
// src/commands/agents.ts
|
|
5860
|
-
var CONFIG_FILE = ".lynxprompt/conf.yml";
|
|
5861
|
-
async function agentsCommand(action, agentId, options = {}) {
|
|
5862
|
-
console.log();
|
|
5863
|
-
switch (action) {
|
|
5864
|
-
case "enable":
|
|
5865
|
-
await enableAgent(agentId, options);
|
|
5866
|
-
break;
|
|
5867
|
-
case "disable":
|
|
5868
|
-
await disableAgent(agentId);
|
|
5869
|
-
break;
|
|
5870
|
-
case "detect":
|
|
5871
|
-
await detectAgentsInProject();
|
|
5872
|
-
break;
|
|
5873
|
-
case "list":
|
|
5874
|
-
default:
|
|
5875
|
-
await listAgents();
|
|
5876
|
-
break;
|
|
5877
|
-
}
|
|
5878
|
-
}
|
|
5879
|
-
async function listAgents() {
|
|
5880
|
-
console.log(chalk10.cyan("\u{1F431} LynxPrompt Agents"));
|
|
5881
|
-
console.log();
|
|
5882
|
-
const config2 = await loadConfig();
|
|
5883
|
-
const enabledSet = new Set(config2?.exporters ?? []);
|
|
5884
|
-
const detection = detectAgents();
|
|
5885
|
-
if (enabledSet.size > 0) {
|
|
5886
|
-
console.log(chalk10.green("Enabled:"));
|
|
5887
|
-
for (const id of enabledSet) {
|
|
5888
|
-
const agent = getAgent(id);
|
|
5889
|
-
const detected = detection.detected.find((d) => d.agent.id === id);
|
|
5890
|
-
const status = detected ? chalk10.gray("(detected)") : "";
|
|
5891
|
-
console.log(` ${chalk10.green("\u2713")} ${agent?.name ?? id} ${status}`);
|
|
5892
|
-
}
|
|
5893
|
-
console.log();
|
|
5894
|
-
}
|
|
5895
|
-
const detectedNotEnabled = detection.detected.filter(
|
|
5896
|
-
(d) => !enabledSet.has(d.agent.id)
|
|
5897
|
-
);
|
|
5898
|
-
if (detectedNotEnabled.length > 0) {
|
|
5899
|
-
console.log(chalk10.yellow("Detected (not enabled):"));
|
|
5900
|
-
for (const detected of detectedNotEnabled) {
|
|
5901
|
-
const rules = detected.ruleCount > 0 ? chalk10.gray(` (${detected.ruleCount} rules)`) : "";
|
|
5902
|
-
console.log(` ${chalk10.yellow("\u25CB")} ${detected.agent.name}${rules}`);
|
|
5903
|
-
}
|
|
5904
|
-
console.log();
|
|
5905
|
-
}
|
|
5906
|
-
const popular = getPopularAgents().filter(
|
|
5907
|
-
(a) => !enabledSet.has(a.id) && !detectedNotEnabled.some((d) => d.agent.id === a.id)
|
|
5908
|
-
);
|
|
5909
|
-
if (popular.length > 0) {
|
|
5910
|
-
console.log(chalk10.gray("Popular (available):"));
|
|
5911
|
-
for (const agent of popular) {
|
|
5912
|
-
console.log(` ${chalk10.gray("-")} ${agent.name} - ${agent.description}`);
|
|
5913
|
-
}
|
|
5914
|
-
console.log();
|
|
5915
|
-
}
|
|
5916
|
-
console.log(chalk10.gray(`Total: ${AGENTS.length} agents supported`));
|
|
5917
|
-
console.log();
|
|
5918
|
-
console.log(chalk10.gray("Commands:"));
|
|
5919
|
-
console.log(chalk10.gray(" lynxp agents enable <agent> Enable an agent"));
|
|
5920
|
-
console.log(chalk10.gray(" lynxp agents disable <agent> Disable an agent"));
|
|
5921
|
-
console.log(chalk10.gray(" lynxp agents detect Auto-detect agents"));
|
|
5922
|
-
console.log();
|
|
5923
|
-
}
|
|
5924
|
-
async function enableAgent(agentId, options = {}) {
|
|
5925
|
-
const cwd = process.cwd();
|
|
5926
|
-
const configPath = join7(cwd, CONFIG_FILE);
|
|
5927
|
-
if (!existsSync4(configPath)) {
|
|
5928
|
-
console.log(chalk10.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
|
|
5929
|
-
return;
|
|
5930
|
-
}
|
|
5931
|
-
let config2 = await loadConfig();
|
|
5932
|
-
if (!config2) {
|
|
5933
|
-
console.log(chalk10.red("Could not load configuration."));
|
|
5934
|
-
return;
|
|
5935
|
-
}
|
|
5936
|
-
if (!agentId || options.interactive) {
|
|
5937
|
-
const enabledSet = new Set(config2.exporters ?? []);
|
|
5938
|
-
const choices = AGENTS.map((agent2) => ({
|
|
5939
|
-
title: `${agent2.name} - ${agent2.description}`,
|
|
5940
|
-
value: agent2.id,
|
|
5941
|
-
selected: enabledSet.has(agent2.id)
|
|
5942
|
-
}));
|
|
5943
|
-
const { selected } = await prompts4({
|
|
5944
|
-
type: "multiselect",
|
|
5945
|
-
name: "selected",
|
|
5946
|
-
message: "Select agents to enable:",
|
|
5947
|
-
choices,
|
|
5948
|
-
hint: "- Space to select, Enter to confirm"
|
|
5949
|
-
});
|
|
5950
|
-
if (!selected || selected.length === 0) {
|
|
5951
|
-
console.log(chalk10.yellow("No agents selected."));
|
|
5952
|
-
return;
|
|
5953
|
-
}
|
|
5954
|
-
config2.exporters = selected;
|
|
5955
|
-
await saveConfig(config2);
|
|
5956
|
-
console.log(chalk10.green(`\u2713 Enabled ${selected.length} agent${selected.length === 1 ? "" : "s"}`));
|
|
5957
|
-
console.log();
|
|
5958
|
-
console.log(chalk10.gray("Run 'lynxp sync' to sync your rules."));
|
|
5959
|
-
return;
|
|
5960
|
-
}
|
|
5961
|
-
const agent = getAgent(agentId);
|
|
5962
|
-
if (!agent) {
|
|
5963
|
-
const similar = AGENTS.filter(
|
|
5964
|
-
(a) => a.id.includes(agentId.toLowerCase()) || a.name.toLowerCase().includes(agentId.toLowerCase())
|
|
5965
|
-
);
|
|
5966
|
-
console.log(chalk10.red(`Unknown agent: ${agentId}`));
|
|
5967
|
-
if (similar.length > 0) {
|
|
5968
|
-
console.log();
|
|
5969
|
-
console.log(chalk10.gray("Did you mean:"));
|
|
5970
|
-
for (const a of similar.slice(0, 5)) {
|
|
5971
|
-
console.log(chalk10.gray(` ${a.id} - ${a.name}`));
|
|
5972
|
-
}
|
|
5973
|
-
}
|
|
5974
|
-
return;
|
|
5975
|
-
}
|
|
5976
|
-
if (!config2.exporters) {
|
|
5977
|
-
config2.exporters = [];
|
|
5978
|
-
}
|
|
5979
|
-
if (config2.exporters.includes(agent.id)) {
|
|
5980
|
-
console.log(chalk10.yellow(`${agent.name} is already enabled.`));
|
|
5981
|
-
return;
|
|
5982
|
-
}
|
|
5983
|
-
config2.exporters.push(agent.id);
|
|
5984
|
-
await saveConfig(config2);
|
|
5985
|
-
console.log(chalk10.green(`\u2713 Enabled ${agent.name}`));
|
|
5986
|
-
console.log();
|
|
5987
|
-
console.log(chalk10.gray(`Output: ${agent.output}`));
|
|
5988
|
-
console.log(chalk10.gray("Run 'lynxp sync' to sync your rules."));
|
|
5989
|
-
}
|
|
5990
|
-
async function disableAgent(agentId) {
|
|
5991
|
-
if (!agentId) {
|
|
5992
|
-
console.log(chalk10.yellow("Usage: lynxp agents disable <agent>"));
|
|
5993
|
-
return;
|
|
5994
|
-
}
|
|
5995
|
-
const cwd = process.cwd();
|
|
5996
|
-
const configPath = join7(cwd, CONFIG_FILE);
|
|
5997
|
-
if (!existsSync4(configPath)) {
|
|
5998
|
-
console.log(chalk10.yellow("LynxPrompt not initialized. Run 'lynxp init' first."));
|
|
5999
|
-
return;
|
|
6000
|
-
}
|
|
6001
|
-
const config2 = await loadConfig();
|
|
6002
|
-
if (!config2) {
|
|
6003
|
-
console.log(chalk10.red("Could not load configuration."));
|
|
6004
|
-
return;
|
|
6005
|
-
}
|
|
6006
|
-
if (!config2.exporters || !config2.exporters.includes(agentId)) {
|
|
6007
|
-
const agent2 = getAgent(agentId);
|
|
6008
|
-
console.log(chalk10.yellow(`${agent2?.name ?? agentId} is not enabled.`));
|
|
6009
|
-
return;
|
|
6010
|
-
}
|
|
6011
|
-
if (config2.exporters.length === 1) {
|
|
6012
|
-
console.log(chalk10.yellow("Cannot disable the last agent. At least one must be enabled."));
|
|
6013
|
-
return;
|
|
6014
|
-
}
|
|
6015
|
-
config2.exporters = config2.exporters.filter((e) => e !== agentId);
|
|
6016
|
-
await saveConfig(config2);
|
|
6017
|
-
const agent = getAgent(agentId);
|
|
6018
|
-
console.log(chalk10.green(`\u2713 Disabled ${agent?.name ?? agentId}`));
|
|
6019
|
-
}
|
|
6020
|
-
async function detectAgentsInProject() {
|
|
6021
|
-
console.log(chalk10.cyan("\u{1F50D} Detecting AI agents..."));
|
|
6022
|
-
console.log();
|
|
6023
|
-
const detection = detectAgents();
|
|
6024
|
-
console.log(formatDetectionResults(detection));
|
|
6025
|
-
console.log();
|
|
6026
|
-
if (detection.detected.length === 0) {
|
|
6027
|
-
return;
|
|
6028
|
-
}
|
|
6029
|
-
const config2 = await loadConfig();
|
|
6030
|
-
const enabledSet = new Set(config2?.exporters ?? []);
|
|
6031
|
-
const newAgents = detection.detected.filter((d) => !enabledSet.has(d.agent.id));
|
|
6032
|
-
if (newAgents.length === 0) {
|
|
6033
|
-
console.log(chalk10.gray("All detected agents are already enabled."));
|
|
6034
|
-
return;
|
|
6035
|
-
}
|
|
6036
|
-
const { enable } = await prompts4({
|
|
6037
|
-
type: "confirm",
|
|
6038
|
-
name: "enable",
|
|
6039
|
-
message: `Enable ${newAgents.length} detected agent${newAgents.length === 1 ? "" : "s"}?`,
|
|
6040
|
-
initial: true
|
|
6041
|
-
});
|
|
6042
|
-
if (enable && config2) {
|
|
6043
|
-
config2.exporters = [
|
|
6044
|
-
...config2.exporters ?? [],
|
|
6045
|
-
...newAgents.map((d) => d.agent.id)
|
|
6046
|
-
];
|
|
6047
|
-
await saveConfig(config2);
|
|
6048
|
-
console.log(chalk10.green(`\u2713 Enabled ${newAgents.length} agent${newAgents.length === 1 ? "" : "s"}`));
|
|
6049
|
-
}
|
|
6050
|
-
}
|
|
6051
|
-
async function loadConfig() {
|
|
6052
|
-
const cwd = process.cwd();
|
|
6053
|
-
const configPath = join7(cwd, CONFIG_FILE);
|
|
6054
|
-
if (!existsSync4(configPath)) {
|
|
6055
|
-
return null;
|
|
6056
|
-
}
|
|
6057
|
-
try {
|
|
6058
|
-
const content = await readFile6(configPath, "utf-8");
|
|
6059
|
-
return yaml2.parse(content);
|
|
6060
|
-
} catch {
|
|
6061
|
-
return null;
|
|
6062
|
-
}
|
|
6063
|
-
}
|
|
6064
|
-
async function saveConfig(config2) {
|
|
6065
|
-
const cwd = process.cwd();
|
|
6066
|
-
const configPath = join7(cwd, CONFIG_FILE);
|
|
6067
|
-
const content = yaml2.stringify(config2);
|
|
6068
|
-
await writeFile4(configPath, content, "utf-8");
|
|
6069
|
-
}
|
|
6070
|
-
|
|
6071
5462
|
// src/commands/check.ts
|
|
6072
|
-
import
|
|
5463
|
+
import chalk10 from "chalk";
|
|
6073
5464
|
import ora8 from "ora";
|
|
6074
|
-
import { readFile as
|
|
6075
|
-
import { join as
|
|
6076
|
-
import { existsSync as
|
|
6077
|
-
import * as
|
|
5465
|
+
import { readFile as readFile6, readdir as readdir2, stat } from "fs/promises";
|
|
5466
|
+
import { join as join6 } from "path";
|
|
5467
|
+
import { existsSync as existsSync3 } from "fs";
|
|
5468
|
+
import * as yaml2 from "yaml";
|
|
6078
5469
|
var CONFIG_FILES2 = [
|
|
6079
5470
|
{ path: "AGENTS.md", name: "AGENTS.md" },
|
|
6080
5471
|
{ path: "CLAUDE.md", name: "CLAUDE.md" },
|
|
@@ -6132,13 +5523,13 @@ function validateMarkdown(content, filename) {
|
|
|
6132
5523
|
async function validateLynxPromptConfig(cwd) {
|
|
6133
5524
|
const errors = [];
|
|
6134
5525
|
const warnings = [];
|
|
6135
|
-
const configPath =
|
|
6136
|
-
if (!
|
|
5526
|
+
const configPath = join6(cwd, ".lynxprompt/conf.yml");
|
|
5527
|
+
if (!existsSync3(configPath)) {
|
|
6137
5528
|
return { errors, warnings };
|
|
6138
5529
|
}
|
|
6139
5530
|
try {
|
|
6140
|
-
const content = await
|
|
6141
|
-
const config2 =
|
|
5531
|
+
const content = await readFile6(configPath, "utf-8");
|
|
5532
|
+
const config2 = yaml2.parse(content);
|
|
6142
5533
|
if (!config2.version) {
|
|
6143
5534
|
warnings.push(".lynxprompt/conf.yml: Missing 'version' field");
|
|
6144
5535
|
}
|
|
@@ -6152,8 +5543,8 @@ async function validateLynxPromptConfig(cwd) {
|
|
|
6152
5543
|
} else {
|
|
6153
5544
|
for (const source of config2.sources) {
|
|
6154
5545
|
if (source.type === "local" && source.path) {
|
|
6155
|
-
const sourcePath =
|
|
6156
|
-
if (!
|
|
5546
|
+
const sourcePath = join6(cwd, source.path);
|
|
5547
|
+
if (!existsSync3(sourcePath)) {
|
|
6157
5548
|
errors.push(`.lynxprompt/conf.yml: Source path not found: ${source.path}`);
|
|
6158
5549
|
}
|
|
6159
5550
|
}
|
|
@@ -6176,7 +5567,7 @@ function validateMdc(content, filename) {
|
|
|
6176
5567
|
} else {
|
|
6177
5568
|
const frontmatter = content.substring(3, frontmatterEnd).trim();
|
|
6178
5569
|
try {
|
|
6179
|
-
|
|
5570
|
+
yaml2.parse(frontmatter);
|
|
6180
5571
|
} catch {
|
|
6181
5572
|
errors.push(`${filename}: Invalid YAML frontmatter`);
|
|
6182
5573
|
}
|
|
@@ -6195,7 +5586,7 @@ async function checkCommand(options = {}) {
|
|
|
6195
5586
|
const cwd = process.cwd();
|
|
6196
5587
|
if (!isCi) {
|
|
6197
5588
|
console.log();
|
|
6198
|
-
console.log(
|
|
5589
|
+
console.log(chalk10.cyan("\u{1F431} LynxPrompt Check"));
|
|
6199
5590
|
console.log();
|
|
6200
5591
|
}
|
|
6201
5592
|
const result = {
|
|
@@ -6206,11 +5597,11 @@ async function checkCommand(options = {}) {
|
|
|
6206
5597
|
};
|
|
6207
5598
|
const spinner = !isCi ? ora8("Scanning for configuration files...").start() : null;
|
|
6208
5599
|
for (const file of CONFIG_FILES2) {
|
|
6209
|
-
const filePath =
|
|
6210
|
-
if (
|
|
5600
|
+
const filePath = join6(cwd, file.path);
|
|
5601
|
+
if (existsSync3(filePath)) {
|
|
6211
5602
|
result.files.push(file.path);
|
|
6212
5603
|
try {
|
|
6213
|
-
const content = await
|
|
5604
|
+
const content = await readFile6(filePath, "utf-8");
|
|
6214
5605
|
const validation = validateMarkdown(content, file.path);
|
|
6215
5606
|
result.errors.push(...validation.errors);
|
|
6216
5607
|
result.warnings.push(...validation.warnings);
|
|
@@ -6220,16 +5611,16 @@ async function checkCommand(options = {}) {
|
|
|
6220
5611
|
}
|
|
6221
5612
|
}
|
|
6222
5613
|
for (const dir of CONFIG_DIRS2) {
|
|
6223
|
-
const dirPath =
|
|
6224
|
-
if (
|
|
5614
|
+
const dirPath = join6(cwd, dir.path);
|
|
5615
|
+
if (existsSync3(dirPath)) {
|
|
6225
5616
|
try {
|
|
6226
5617
|
const files = await readdir2(dirPath);
|
|
6227
5618
|
for (const file of files) {
|
|
6228
|
-
const filePath =
|
|
5619
|
+
const filePath = join6(dirPath, file);
|
|
6229
5620
|
const fileStat = await stat(filePath);
|
|
6230
5621
|
if (fileStat.isFile()) {
|
|
6231
5622
|
result.files.push(`${dir.path}/${file}`);
|
|
6232
|
-
const content = await
|
|
5623
|
+
const content = await readFile6(filePath, "utf-8");
|
|
6233
5624
|
if (file.endsWith(".mdc")) {
|
|
6234
5625
|
const validation = validateMdc(content, `${dir.path}/${file}`);
|
|
6235
5626
|
result.errors.push(...validation.errors);
|
|
@@ -6269,45 +5660,45 @@ async function checkCommand(options = {}) {
|
|
|
6269
5660
|
}
|
|
6270
5661
|
} else {
|
|
6271
5662
|
if (result.files.length === 0) {
|
|
6272
|
-
console.log(
|
|
5663
|
+
console.log(chalk10.yellow("\u26A0 No AI configuration files found."));
|
|
6273
5664
|
console.log();
|
|
6274
|
-
console.log(
|
|
5665
|
+
console.log(chalk10.gray("Run 'lynxp wizard' to create a configuration."));
|
|
6275
5666
|
return;
|
|
6276
5667
|
}
|
|
6277
|
-
console.log(
|
|
5668
|
+
console.log(chalk10.green(`\u2713 Found ${result.files.length} configuration file${result.files.length === 1 ? "" : "s"}:`));
|
|
6278
5669
|
for (const file of result.files) {
|
|
6279
|
-
console.log(
|
|
5670
|
+
console.log(chalk10.gray(` ${file}`));
|
|
6280
5671
|
}
|
|
6281
5672
|
console.log();
|
|
6282
5673
|
if (result.errors.length > 0) {
|
|
6283
|
-
console.log(
|
|
5674
|
+
console.log(chalk10.red(`\u2717 ${result.errors.length} error${result.errors.length === 1 ? "" : "s"}:`));
|
|
6284
5675
|
for (const error of result.errors) {
|
|
6285
|
-
console.log(
|
|
5676
|
+
console.log(chalk10.red(` ${error}`));
|
|
6286
5677
|
}
|
|
6287
5678
|
console.log();
|
|
6288
5679
|
}
|
|
6289
5680
|
if (result.warnings.length > 0) {
|
|
6290
|
-
console.log(
|
|
5681
|
+
console.log(chalk10.yellow(`\u26A0 ${result.warnings.length} warning${result.warnings.length === 1 ? "" : "s"}:`));
|
|
6291
5682
|
for (const warning of result.warnings) {
|
|
6292
|
-
console.log(
|
|
5683
|
+
console.log(chalk10.yellow(` ${warning}`));
|
|
6293
5684
|
}
|
|
6294
5685
|
console.log();
|
|
6295
5686
|
}
|
|
6296
5687
|
if (result.valid) {
|
|
6297
|
-
console.log(
|
|
5688
|
+
console.log(chalk10.green("\u2705 Validation passed!"));
|
|
6298
5689
|
} else {
|
|
6299
|
-
console.log(
|
|
5690
|
+
console.log(chalk10.red("\u274C Validation failed. Fix the errors above."));
|
|
6300
5691
|
}
|
|
6301
5692
|
console.log();
|
|
6302
5693
|
}
|
|
6303
5694
|
}
|
|
6304
5695
|
|
|
6305
5696
|
// src/commands/diff.ts
|
|
6306
|
-
import
|
|
5697
|
+
import chalk11 from "chalk";
|
|
6307
5698
|
import ora9 from "ora";
|
|
6308
|
-
import { readFile as
|
|
6309
|
-
import { join as
|
|
6310
|
-
import { existsSync as
|
|
5699
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
5700
|
+
import { join as join7 } from "path";
|
|
5701
|
+
import { existsSync as existsSync4 } from "fs";
|
|
6311
5702
|
function computeDiff(oldText, newText) {
|
|
6312
5703
|
const oldLines = oldText.split("\n");
|
|
6313
5704
|
const newLines = newText.split("\n");
|
|
@@ -6371,21 +5762,21 @@ function formatDiff(diff, contextLines = 3) {
|
|
|
6371
5762
|
let lastPrintedIndex = -1;
|
|
6372
5763
|
const changeIndices = diff.map((d, i) => d.type !== "same" ? i : -1).filter((i) => i !== -1);
|
|
6373
5764
|
if (changeIndices.length === 0) {
|
|
6374
|
-
return
|
|
5765
|
+
return chalk11.gray(" (no changes)");
|
|
6375
5766
|
}
|
|
6376
5767
|
for (let i = 0; i < diff.length; i++) {
|
|
6377
5768
|
const item = diff[i];
|
|
6378
5769
|
const nearChange = changeIndices.some((ci) => Math.abs(ci - i) <= contextLines);
|
|
6379
5770
|
if (nearChange) {
|
|
6380
5771
|
if (lastPrintedIndex !== -1 && i - lastPrintedIndex > 1) {
|
|
6381
|
-
output.push(
|
|
5772
|
+
output.push(chalk11.gray(" ..."));
|
|
6382
5773
|
}
|
|
6383
5774
|
if (item.type === "add") {
|
|
6384
|
-
output.push(
|
|
5775
|
+
output.push(chalk11.green(`+ ${item.line}`));
|
|
6385
5776
|
} else if (item.type === "remove") {
|
|
6386
|
-
output.push(
|
|
5777
|
+
output.push(chalk11.red(`- ${item.line}`));
|
|
6387
5778
|
} else {
|
|
6388
|
-
output.push(
|
|
5779
|
+
output.push(chalk11.gray(` ${item.line}`));
|
|
6389
5780
|
}
|
|
6390
5781
|
lastPrintedIndex = i;
|
|
6391
5782
|
}
|
|
@@ -6401,7 +5792,7 @@ function getDiffStats(diff) {
|
|
|
6401
5792
|
}
|
|
6402
5793
|
async function diffCommand(fileOrId, options = {}) {
|
|
6403
5794
|
console.log();
|
|
6404
|
-
console.log(
|
|
5795
|
+
console.log(chalk11.cyan("\u{1F431} LynxPrompt Diff"));
|
|
6405
5796
|
console.log();
|
|
6406
5797
|
const cwd = process.cwd();
|
|
6407
5798
|
if (options.local) {
|
|
@@ -6419,44 +5810,44 @@ async function diffCommand(fileOrId, options = {}) {
|
|
|
6419
5810
|
return;
|
|
6420
5811
|
}
|
|
6421
5812
|
if (trackedFiles.length === 0) {
|
|
6422
|
-
console.log(
|
|
5813
|
+
console.log(chalk11.yellow("No tracked blueprints found."));
|
|
6423
5814
|
console.log();
|
|
6424
|
-
console.log(
|
|
6425
|
-
console.log(
|
|
6426
|
-
console.log(
|
|
5815
|
+
console.log(chalk11.gray("To track a blueprint and compare changes:"));
|
|
5816
|
+
console.log(chalk11.gray(" 1. Pull a blueprint: lynxp pull <blueprint-id>"));
|
|
5817
|
+
console.log(chalk11.gray(" 2. Or link an existing file: lynxp link"));
|
|
6427
5818
|
console.log();
|
|
6428
|
-
console.log(
|
|
6429
|
-
console.log(
|
|
5819
|
+
console.log(chalk11.gray("Other options:"));
|
|
5820
|
+
console.log(chalk11.gray(" lynxp diff --local Compare .lynxprompt/rules/ with exported files"));
|
|
6430
5821
|
return;
|
|
6431
5822
|
}
|
|
6432
5823
|
let hasChanges = false;
|
|
6433
5824
|
for (const { blueprint, localModified, fileExists: fileExists2 } of trackedFiles) {
|
|
6434
5825
|
if (!fileExists2) {
|
|
6435
|
-
console.log(
|
|
5826
|
+
console.log(chalk11.red(`\u2717 ${blueprint.file} - file not found`));
|
|
6436
5827
|
continue;
|
|
6437
5828
|
}
|
|
6438
|
-
console.log(
|
|
6439
|
-
console.log(
|
|
5829
|
+
console.log(chalk11.cyan(`\u{1F4C4} ${blueprint.file}`));
|
|
5830
|
+
console.log(chalk11.gray(` Linked to: ${blueprint.name} (${blueprint.id})`));
|
|
6440
5831
|
if (localModified) {
|
|
6441
5832
|
hasChanges = true;
|
|
6442
5833
|
await diffFileWithBlueprint(cwd, blueprint.file, blueprint.id, true);
|
|
6443
5834
|
} else {
|
|
6444
|
-
console.log(
|
|
5835
|
+
console.log(chalk11.green(" \u2713 In sync with cloud"));
|
|
6445
5836
|
}
|
|
6446
5837
|
console.log();
|
|
6447
5838
|
}
|
|
6448
5839
|
if (!hasChanges) {
|
|
6449
|
-
console.log(
|
|
5840
|
+
console.log(chalk11.green("\u2713 All tracked files are in sync with their cloud blueprints!"));
|
|
6450
5841
|
} else {
|
|
6451
|
-
console.log(
|
|
6452
|
-
console.log(
|
|
5842
|
+
console.log(chalk11.gray("To push local changes: lynxp push"));
|
|
5843
|
+
console.log(chalk11.gray("To pull cloud changes: lynxp pull <id>"));
|
|
6453
5844
|
}
|
|
6454
5845
|
console.log();
|
|
6455
5846
|
}
|
|
6456
5847
|
async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false) {
|
|
6457
|
-
const filePath =
|
|
6458
|
-
if (!
|
|
6459
|
-
console.log(
|
|
5848
|
+
const filePath = join7(cwd, file);
|
|
5849
|
+
if (!existsSync4(filePath)) {
|
|
5850
|
+
console.log(chalk11.red(`\u2717 File not found: ${file}`));
|
|
6460
5851
|
return;
|
|
6461
5852
|
}
|
|
6462
5853
|
const spinner = compact ? null : ora9("Fetching blueprint...").start();
|
|
@@ -6464,37 +5855,37 @@ async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false) {
|
|
|
6464
5855
|
const { blueprint } = await api.getBlueprint(blueprintId);
|
|
6465
5856
|
spinner?.stop();
|
|
6466
5857
|
if (!blueprint || !blueprint.content) {
|
|
6467
|
-
console.log(
|
|
5858
|
+
console.log(chalk11.red(`\u2717 Blueprint has no content`));
|
|
6468
5859
|
return;
|
|
6469
5860
|
}
|
|
6470
|
-
const localContent = await
|
|
5861
|
+
const localContent = await readFile7(filePath, "utf-8");
|
|
6471
5862
|
const diff = computeDiff(blueprint.content, localContent);
|
|
6472
5863
|
const stats = getDiffStats(diff);
|
|
6473
5864
|
if (stats.added === 0 && stats.removed === 0) {
|
|
6474
5865
|
if (!compact) {
|
|
6475
|
-
console.log(
|
|
5866
|
+
console.log(chalk11.green("\u2713 Files are identical!"));
|
|
6476
5867
|
}
|
|
6477
5868
|
} else {
|
|
6478
5869
|
if (!compact) {
|
|
6479
|
-
console.log(
|
|
5870
|
+
console.log(chalk11.gray("Changes (cloud \u2192 local):"));
|
|
6480
5871
|
console.log();
|
|
6481
5872
|
}
|
|
6482
5873
|
console.log(formatDiff(diff));
|
|
6483
|
-
console.log(
|
|
5874
|
+
console.log(chalk11.gray(` ${chalk11.green(`+${stats.added}`)} ${chalk11.red(`-${stats.removed}`)} lines`));
|
|
6484
5875
|
}
|
|
6485
5876
|
} catch (error) {
|
|
6486
5877
|
spinner?.stop();
|
|
6487
5878
|
if (error instanceof ApiRequestError) {
|
|
6488
|
-
console.log(
|
|
5879
|
+
console.log(chalk11.red(`\u2717 Could not fetch blueprint: ${error.message}`));
|
|
6489
5880
|
} else {
|
|
6490
|
-
console.log(
|
|
5881
|
+
console.log(chalk11.red("\u2717 Failed to compare"));
|
|
6491
5882
|
}
|
|
6492
5883
|
}
|
|
6493
5884
|
}
|
|
6494
5885
|
async function diffWithBlueprintId(cwd, blueprintId) {
|
|
6495
5886
|
if (!isAuthenticated()) {
|
|
6496
|
-
console.log(
|
|
6497
|
-
console.log(
|
|
5887
|
+
console.log(chalk11.yellow("\u26A0 Not logged in. Some blueprints may not be accessible."));
|
|
5888
|
+
console.log(chalk11.gray("Run 'lynxp login' to authenticate."));
|
|
6498
5889
|
console.log();
|
|
6499
5890
|
}
|
|
6500
5891
|
const spinner = ora9("Fetching blueprint...").start();
|
|
@@ -6502,12 +5893,12 @@ async function diffWithBlueprintId(cwd, blueprintId) {
|
|
|
6502
5893
|
const { blueprint } = await api.getBlueprint(blueprintId);
|
|
6503
5894
|
spinner.stop();
|
|
6504
5895
|
if (!blueprint || !blueprint.content) {
|
|
6505
|
-
console.log(
|
|
5896
|
+
console.log(chalk11.red(`\u2717 Blueprint not found or has no content: ${blueprintId}`));
|
|
6506
5897
|
return;
|
|
6507
5898
|
}
|
|
6508
|
-
console.log(
|
|
5899
|
+
console.log(chalk11.green(`\u2713 Blueprint: ${blueprint.name || blueprintId}`));
|
|
6509
5900
|
if (blueprint.description) {
|
|
6510
|
-
console.log(
|
|
5901
|
+
console.log(chalk11.gray(` ${blueprint.description}`));
|
|
6511
5902
|
}
|
|
6512
5903
|
console.log();
|
|
6513
5904
|
const localPaths = [
|
|
@@ -6520,10 +5911,10 @@ async function diffWithBlueprintId(cwd, blueprintId) {
|
|
|
6520
5911
|
let localContent = null;
|
|
6521
5912
|
let localPath = null;
|
|
6522
5913
|
for (const path2 of localPaths) {
|
|
6523
|
-
const fullPath =
|
|
6524
|
-
if (
|
|
5914
|
+
const fullPath = join7(cwd, path2);
|
|
5915
|
+
if (existsSync4(fullPath)) {
|
|
6525
5916
|
try {
|
|
6526
|
-
localContent = await
|
|
5917
|
+
localContent = await readFile7(fullPath, "utf-8");
|
|
6527
5918
|
localPath = path2;
|
|
6528
5919
|
break;
|
|
6529
5920
|
} catch {
|
|
@@ -6531,63 +5922,63 @@ async function diffWithBlueprintId(cwd, blueprintId) {
|
|
|
6531
5922
|
}
|
|
6532
5923
|
}
|
|
6533
5924
|
if (!localContent) {
|
|
6534
|
-
console.log(
|
|
6535
|
-
console.log(
|
|
5925
|
+
console.log(chalk11.yellow("\u26A0 No local AI configuration file found."));
|
|
5926
|
+
console.log(chalk11.gray("Run 'lynxp wizard' to create one, or 'lynxp pull' to download the blueprint."));
|
|
6536
5927
|
return;
|
|
6537
5928
|
}
|
|
6538
|
-
console.log(
|
|
5929
|
+
console.log(chalk11.gray(`Comparing with: ${localPath}`));
|
|
6539
5930
|
console.log();
|
|
6540
5931
|
const diff = computeDiff(blueprint.content, localContent);
|
|
6541
5932
|
const stats = getDiffStats(diff);
|
|
6542
5933
|
if (stats.added === 0 && stats.removed === 0) {
|
|
6543
|
-
console.log(
|
|
5934
|
+
console.log(chalk11.green("\u2713 Files are identical!"));
|
|
6544
5935
|
} else {
|
|
6545
|
-
console.log(
|
|
5936
|
+
console.log(chalk11.gray("Changes (remote \u2192 local):"));
|
|
6546
5937
|
console.log();
|
|
6547
5938
|
console.log(formatDiff(diff));
|
|
6548
5939
|
console.log();
|
|
6549
|
-
console.log(
|
|
5940
|
+
console.log(chalk11.gray(`Summary: ${chalk11.green(`+${stats.added}`)} ${chalk11.red(`-${stats.removed}`)} lines changed`));
|
|
6550
5941
|
}
|
|
6551
5942
|
console.log();
|
|
6552
5943
|
} catch (error) {
|
|
6553
5944
|
spinner.stop();
|
|
6554
5945
|
if (error instanceof ApiRequestError) {
|
|
6555
5946
|
if (error.statusCode === 401) {
|
|
6556
|
-
console.log(
|
|
5947
|
+
console.log(chalk11.red("\u2717 Authentication required. Run 'lynxp login' first."));
|
|
6557
5948
|
} else if (error.statusCode === 404) {
|
|
6558
|
-
console.log(
|
|
5949
|
+
console.log(chalk11.red(`\u2717 Blueprint not found: ${blueprintId}`));
|
|
6559
5950
|
} else if (error.statusCode === 403) {
|
|
6560
|
-
console.log(
|
|
5951
|
+
console.log(chalk11.red("\u2717 Access denied to this blueprint."));
|
|
6561
5952
|
} else {
|
|
6562
|
-
console.log(
|
|
5953
|
+
console.log(chalk11.red(`\u2717 API error: ${error.message}`));
|
|
6563
5954
|
}
|
|
6564
5955
|
} else {
|
|
6565
|
-
console.log(
|
|
5956
|
+
console.log(chalk11.red("\u2717 Failed to fetch blueprint"));
|
|
6566
5957
|
if (error instanceof Error) {
|
|
6567
|
-
console.log(
|
|
5958
|
+
console.log(chalk11.gray(` ${error.message}`));
|
|
6568
5959
|
}
|
|
6569
5960
|
}
|
|
6570
5961
|
}
|
|
6571
5962
|
}
|
|
6572
5963
|
async function diffLocal(cwd) {
|
|
6573
|
-
const rulesDir =
|
|
6574
|
-
if (!
|
|
6575
|
-
console.log(
|
|
6576
|
-
console.log(
|
|
5964
|
+
const rulesDir = join7(cwd, ".lynxprompt/rules");
|
|
5965
|
+
if (!existsSync4(rulesDir)) {
|
|
5966
|
+
console.log(chalk11.yellow("\u26A0 No .lynxprompt/rules/ directory found."));
|
|
5967
|
+
console.log(chalk11.gray("Run 'lynxp init' to set up the advanced workflow, or 'lynxp wizard' for simple file generation."));
|
|
6577
5968
|
return;
|
|
6578
5969
|
}
|
|
6579
|
-
console.log(
|
|
5970
|
+
console.log(chalk11.gray("Comparing .lynxprompt/rules/ with exported files..."));
|
|
6580
5971
|
console.log();
|
|
6581
|
-
const rulesPath =
|
|
6582
|
-
if (!
|
|
6583
|
-
console.log(
|
|
5972
|
+
const rulesPath = join7(rulesDir, "agents.md");
|
|
5973
|
+
if (!existsSync4(rulesPath)) {
|
|
5974
|
+
console.log(chalk11.yellow("\u26A0 No rules files found in .lynxprompt/rules/"));
|
|
6584
5975
|
return;
|
|
6585
5976
|
}
|
|
6586
5977
|
let rulesContent;
|
|
6587
5978
|
try {
|
|
6588
|
-
rulesContent = await
|
|
5979
|
+
rulesContent = await readFile7(rulesPath, "utf-8");
|
|
6589
5980
|
} catch {
|
|
6590
|
-
console.log(
|
|
5981
|
+
console.log(chalk11.red("\u2717 Could not read .lynxprompt/rules/agents.md"));
|
|
6591
5982
|
return;
|
|
6592
5983
|
}
|
|
6593
5984
|
const exportedFiles = [
|
|
@@ -6596,10 +5987,10 @@ async function diffLocal(cwd) {
|
|
|
6596
5987
|
];
|
|
6597
5988
|
let hasChanges = false;
|
|
6598
5989
|
for (const file of exportedFiles) {
|
|
6599
|
-
const filePath =
|
|
6600
|
-
if (
|
|
5990
|
+
const filePath = join7(cwd, file.path);
|
|
5991
|
+
if (existsSync4(filePath)) {
|
|
6601
5992
|
try {
|
|
6602
|
-
const exportedContent = await
|
|
5993
|
+
const exportedContent = await readFile7(filePath, "utf-8");
|
|
6603
5994
|
let compareContent = exportedContent;
|
|
6604
5995
|
if (file.path.endsWith(".mdc")) {
|
|
6605
5996
|
const frontmatterEnd = exportedContent.indexOf("---", 3);
|
|
@@ -6612,12 +6003,12 @@ async function diffLocal(cwd) {
|
|
|
6612
6003
|
const stats = getDiffStats(diff);
|
|
6613
6004
|
if (stats.added > 0 || stats.removed > 0) {
|
|
6614
6005
|
hasChanges = true;
|
|
6615
|
-
console.log(
|
|
6006
|
+
console.log(chalk11.yellow(`\u26A0 ${file.name} differs from source:`));
|
|
6616
6007
|
console.log(formatDiff(diff));
|
|
6617
|
-
console.log(
|
|
6008
|
+
console.log(chalk11.gray(` ${chalk11.green(`+${stats.added}`)} ${chalk11.red(`-${stats.removed}`)} lines`));
|
|
6618
6009
|
console.log();
|
|
6619
6010
|
} else {
|
|
6620
|
-
console.log(
|
|
6011
|
+
console.log(chalk11.green(`\u2713 ${file.name} is in sync`));
|
|
6621
6012
|
}
|
|
6622
6013
|
} catch {
|
|
6623
6014
|
}
|
|
@@ -6625,20 +6016,20 @@ async function diffLocal(cwd) {
|
|
|
6625
6016
|
}
|
|
6626
6017
|
if (!hasChanges) {
|
|
6627
6018
|
console.log();
|
|
6628
|
-
console.log(
|
|
6019
|
+
console.log(chalk11.green("\u2713 All exported files are in sync with .lynxprompt/rules/"));
|
|
6629
6020
|
} else {
|
|
6630
6021
|
console.log();
|
|
6631
|
-
console.log(
|
|
6022
|
+
console.log(chalk11.gray("Run 'lynxp sync' to update exported files from .lynxprompt/rules/"));
|
|
6632
6023
|
}
|
|
6633
6024
|
console.log();
|
|
6634
6025
|
}
|
|
6635
6026
|
|
|
6636
6027
|
// src/commands/link.ts
|
|
6637
|
-
import
|
|
6028
|
+
import chalk12 from "chalk";
|
|
6638
6029
|
import ora10 from "ora";
|
|
6639
|
-
import
|
|
6640
|
-
import { join as
|
|
6641
|
-
import { existsSync as
|
|
6030
|
+
import prompts4 from "prompts";
|
|
6031
|
+
import { join as join8 } from "path";
|
|
6032
|
+
import { existsSync as existsSync5 } from "fs";
|
|
6642
6033
|
function getSourceFromVisibility2(visibility) {
|
|
6643
6034
|
switch (visibility) {
|
|
6644
6035
|
case "PUBLIC":
|
|
@@ -6658,7 +6049,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
6658
6049
|
return;
|
|
6659
6050
|
}
|
|
6660
6051
|
console.log();
|
|
6661
|
-
console.log(
|
|
6052
|
+
console.log(chalk12.cyan("\u{1F431} Link File to Blueprint"));
|
|
6662
6053
|
console.log();
|
|
6663
6054
|
let file;
|
|
6664
6055
|
let blueprintId = blueprintIdArg;
|
|
@@ -6672,16 +6063,16 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
6672
6063
|
".zed/instructions.md",
|
|
6673
6064
|
".clinerules"
|
|
6674
6065
|
];
|
|
6675
|
-
const foundFiles = configFiles.filter((f) =>
|
|
6066
|
+
const foundFiles = configFiles.filter((f) => existsSync5(join8(cwd, f)));
|
|
6676
6067
|
if (foundFiles.length === 0) {
|
|
6677
|
-
console.log(
|
|
6068
|
+
console.log(chalk12.yellow("No AI configuration files found in this directory."));
|
|
6678
6069
|
console.log();
|
|
6679
|
-
console.log(
|
|
6680
|
-
console.log(
|
|
6681
|
-
console.log(
|
|
6070
|
+
console.log(chalk12.gray("Create one first:"));
|
|
6071
|
+
console.log(chalk12.gray(" lynxp wizard Generate a new config file"));
|
|
6072
|
+
console.log(chalk12.gray(" lynxp pull <id> Download from marketplace"));
|
|
6682
6073
|
return;
|
|
6683
6074
|
}
|
|
6684
|
-
const { selectedFile } = await
|
|
6075
|
+
const { selectedFile } = await prompts4({
|
|
6685
6076
|
type: "select",
|
|
6686
6077
|
name: "selectedFile",
|
|
6687
6078
|
message: "Which file do you want to link to a cloud blueprint?",
|
|
@@ -6692,51 +6083,51 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
6692
6083
|
}))
|
|
6693
6084
|
});
|
|
6694
6085
|
if (!selectedFile) {
|
|
6695
|
-
console.log(
|
|
6086
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6696
6087
|
return;
|
|
6697
6088
|
}
|
|
6698
6089
|
file = selectedFile;
|
|
6699
6090
|
} else {
|
|
6700
6091
|
file = fileArg;
|
|
6701
6092
|
}
|
|
6702
|
-
const filePath =
|
|
6703
|
-
if (!
|
|
6704
|
-
console.log(
|
|
6093
|
+
const filePath = join8(cwd, file);
|
|
6094
|
+
if (!existsSync5(filePath)) {
|
|
6095
|
+
console.log(chalk12.red(`\u2717 File not found: ${file}`));
|
|
6705
6096
|
return;
|
|
6706
6097
|
}
|
|
6707
6098
|
const existing = await findBlueprintByFile(cwd, file);
|
|
6708
6099
|
if (existing) {
|
|
6709
|
-
console.log(
|
|
6710
|
-
console.log(
|
|
6100
|
+
console.log(chalk12.yellow(`This file is already linked to: ${existing.name}`));
|
|
6101
|
+
console.log(chalk12.gray(` ID: ${existing.id}`));
|
|
6711
6102
|
console.log();
|
|
6712
|
-
const { proceed } = await
|
|
6103
|
+
const { proceed } = await prompts4({
|
|
6713
6104
|
type: "confirm",
|
|
6714
6105
|
name: "proceed",
|
|
6715
6106
|
message: "Replace the existing link?",
|
|
6716
6107
|
initial: false
|
|
6717
6108
|
});
|
|
6718
6109
|
if (!proceed) {
|
|
6719
|
-
console.log(
|
|
6110
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6720
6111
|
return;
|
|
6721
6112
|
}
|
|
6722
6113
|
}
|
|
6723
6114
|
if (!blueprintId) {
|
|
6724
6115
|
if (!isAuthenticated()) {
|
|
6725
|
-
console.log(
|
|
6726
|
-
const { doLogin } = await
|
|
6116
|
+
console.log(chalk12.yellow("You need to login to access your blueprints."));
|
|
6117
|
+
const { doLogin } = await prompts4({
|
|
6727
6118
|
type: "confirm",
|
|
6728
6119
|
name: "doLogin",
|
|
6729
6120
|
message: "Login now?",
|
|
6730
6121
|
initial: true
|
|
6731
6122
|
});
|
|
6732
6123
|
if (doLogin) {
|
|
6733
|
-
console.log(
|
|
6124
|
+
console.log(chalk12.gray("Run 'lynxp login' in another terminal, then come back here."));
|
|
6734
6125
|
return;
|
|
6735
6126
|
}
|
|
6736
|
-
console.log(
|
|
6127
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6737
6128
|
return;
|
|
6738
6129
|
}
|
|
6739
|
-
const { searchMethod } = await
|
|
6130
|
+
const { searchMethod } = await prompts4({
|
|
6740
6131
|
type: "select",
|
|
6741
6132
|
name: "searchMethod",
|
|
6742
6133
|
message: "How do you want to find the blueprint?",
|
|
@@ -6747,7 +6138,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
6747
6138
|
]
|
|
6748
6139
|
});
|
|
6749
6140
|
if (!searchMethod) {
|
|
6750
|
-
console.log(
|
|
6141
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6751
6142
|
return;
|
|
6752
6143
|
}
|
|
6753
6144
|
if (searchMethod === "list") {
|
|
@@ -6756,11 +6147,11 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
6756
6147
|
const { blueprints } = await api.listBlueprints();
|
|
6757
6148
|
spinner2.stop();
|
|
6758
6149
|
if (!blueprints || blueprints.length === 0) {
|
|
6759
|
-
console.log(
|
|
6760
|
-
console.log(
|
|
6150
|
+
console.log(chalk12.yellow("You don't have any blueprints yet."));
|
|
6151
|
+
console.log(chalk12.gray("Create one with 'lynxp push' or search the marketplace."));
|
|
6761
6152
|
return;
|
|
6762
6153
|
}
|
|
6763
|
-
const { selected } = await
|
|
6154
|
+
const { selected } = await prompts4({
|
|
6764
6155
|
type: "select",
|
|
6765
6156
|
name: "selected",
|
|
6766
6157
|
message: "Select a blueprint:",
|
|
@@ -6771,23 +6162,23 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
6771
6162
|
}))
|
|
6772
6163
|
});
|
|
6773
6164
|
if (!selected) {
|
|
6774
|
-
console.log(
|
|
6165
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6775
6166
|
return;
|
|
6776
6167
|
}
|
|
6777
6168
|
blueprintId = selected;
|
|
6778
6169
|
} catch {
|
|
6779
6170
|
spinner2.stop();
|
|
6780
|
-
console.log(
|
|
6171
|
+
console.log(chalk12.red("\u2717 Could not fetch blueprints"));
|
|
6781
6172
|
return;
|
|
6782
6173
|
}
|
|
6783
6174
|
} else if (searchMethod === "search") {
|
|
6784
|
-
const { query } = await
|
|
6175
|
+
const { query } = await prompts4({
|
|
6785
6176
|
type: "text",
|
|
6786
6177
|
name: "query",
|
|
6787
6178
|
message: "Search for:"
|
|
6788
6179
|
});
|
|
6789
6180
|
if (!query) {
|
|
6790
|
-
console.log(
|
|
6181
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6791
6182
|
return;
|
|
6792
6183
|
}
|
|
6793
6184
|
const spinner2 = ora10(`Searching for "${query}"...`).start();
|
|
@@ -6795,10 +6186,10 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
6795
6186
|
const results = await api.searchBlueprints(query, 10);
|
|
6796
6187
|
spinner2.stop();
|
|
6797
6188
|
if (!results.templates || results.templates.length === 0) {
|
|
6798
|
-
console.log(
|
|
6189
|
+
console.log(chalk12.yellow(`No blueprints found for "${query}"`));
|
|
6799
6190
|
return;
|
|
6800
6191
|
}
|
|
6801
|
-
const { selected } = await
|
|
6192
|
+
const { selected } = await prompts4({
|
|
6802
6193
|
type: "select",
|
|
6803
6194
|
name: "selected",
|
|
6804
6195
|
message: "Select a blueprint:",
|
|
@@ -6809,101 +6200,101 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
|
|
|
6809
6200
|
}))
|
|
6810
6201
|
});
|
|
6811
6202
|
if (!selected) {
|
|
6812
|
-
console.log(
|
|
6203
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6813
6204
|
return;
|
|
6814
6205
|
}
|
|
6815
6206
|
blueprintId = selected;
|
|
6816
6207
|
} catch {
|
|
6817
6208
|
spinner2.stop();
|
|
6818
|
-
console.log(
|
|
6209
|
+
console.log(chalk12.red("\u2717 Search failed"));
|
|
6819
6210
|
return;
|
|
6820
6211
|
}
|
|
6821
6212
|
} else {
|
|
6822
|
-
const { manualId } = await
|
|
6213
|
+
const { manualId } = await prompts4({
|
|
6823
6214
|
type: "text",
|
|
6824
6215
|
name: "manualId",
|
|
6825
6216
|
message: "Enter blueprint ID:"
|
|
6826
6217
|
});
|
|
6827
6218
|
if (!manualId) {
|
|
6828
|
-
console.log(
|
|
6219
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6829
6220
|
return;
|
|
6830
6221
|
}
|
|
6831
6222
|
blueprintId = manualId;
|
|
6832
6223
|
}
|
|
6833
6224
|
}
|
|
6834
6225
|
if (!blueprintId) {
|
|
6835
|
-
console.log(
|
|
6226
|
+
console.log(chalk12.red("\u2717 No blueprint ID provided."));
|
|
6836
6227
|
return;
|
|
6837
6228
|
}
|
|
6838
|
-
const spinner = ora10(`Fetching blueprint ${
|
|
6229
|
+
const spinner = ora10(`Fetching blueprint ${chalk12.cyan(blueprintId)}...`).start();
|
|
6839
6230
|
try {
|
|
6840
6231
|
const { blueprint } = await api.getBlueprint(blueprintId);
|
|
6841
6232
|
spinner.stop();
|
|
6842
6233
|
const source = getSourceFromVisibility2(blueprint.visibility);
|
|
6843
6234
|
const isMarketplace = source === "marketplace";
|
|
6844
6235
|
console.log();
|
|
6845
|
-
console.log(
|
|
6236
|
+
console.log(chalk12.cyan(`\u{1F431} Blueprint: ${chalk12.bold(blueprint.name)}`));
|
|
6846
6237
|
if (blueprint.description) {
|
|
6847
|
-
console.log(
|
|
6238
|
+
console.log(chalk12.gray(` ${blueprint.description}`));
|
|
6848
6239
|
}
|
|
6849
|
-
console.log(
|
|
6240
|
+
console.log(chalk12.gray(` Visibility: ${blueprint.visibility}`));
|
|
6850
6241
|
console.log();
|
|
6851
6242
|
if (isMarketplace) {
|
|
6852
|
-
console.log(
|
|
6853
|
-
console.log(
|
|
6854
|
-
console.log(
|
|
6243
|
+
console.log(chalk12.yellow("\u26A0 This is a marketplace blueprint."));
|
|
6244
|
+
console.log(chalk12.gray(" Your local changes will NOT sync back to the cloud."));
|
|
6245
|
+
console.log(chalk12.gray(" To make changes, you'll need to create your own copy."));
|
|
6855
6246
|
console.log();
|
|
6856
6247
|
}
|
|
6857
|
-
const { confirm } = await
|
|
6248
|
+
const { confirm } = await prompts4({
|
|
6858
6249
|
type: "confirm",
|
|
6859
6250
|
name: "confirm",
|
|
6860
|
-
message: `Link ${
|
|
6251
|
+
message: `Link ${chalk12.cyan(file)} to ${chalk12.cyan(blueprint.name)}?`,
|
|
6861
6252
|
initial: true
|
|
6862
6253
|
});
|
|
6863
6254
|
if (!confirm) {
|
|
6864
|
-
console.log(
|
|
6255
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6865
6256
|
return;
|
|
6866
6257
|
}
|
|
6867
6258
|
await linkBlueprint(cwd, file, blueprint.id, blueprint.name, source);
|
|
6868
6259
|
console.log();
|
|
6869
|
-
console.log(
|
|
6260
|
+
console.log(chalk12.green(`\u2705 Linked: ${file} \u2192 ${blueprint.id}`));
|
|
6870
6261
|
console.log();
|
|
6871
|
-
console.log(
|
|
6872
|
-
console.log(
|
|
6873
|
-
console.log(
|
|
6262
|
+
console.log(chalk12.gray("Next steps:"));
|
|
6263
|
+
console.log(chalk12.gray(` \u2022 Run 'lynxp diff' to see differences`));
|
|
6264
|
+
console.log(chalk12.gray(` \u2022 Run 'lynxp status' to see all tracked blueprints`));
|
|
6874
6265
|
if (!isMarketplace) {
|
|
6875
|
-
console.log(
|
|
6266
|
+
console.log(chalk12.gray(` \u2022 Run 'lynxp push' to push local changes to cloud`));
|
|
6876
6267
|
}
|
|
6877
6268
|
console.log();
|
|
6878
6269
|
} catch (error) {
|
|
6879
6270
|
spinner.stop();
|
|
6880
6271
|
if (error instanceof ApiRequestError) {
|
|
6881
6272
|
if (error.statusCode === 404) {
|
|
6882
|
-
console.log(
|
|
6883
|
-
console.log(
|
|
6273
|
+
console.log(chalk12.red(`\u2717 Blueprint not found: ${blueprintId}`));
|
|
6274
|
+
console.log(chalk12.gray(" Make sure the ID is correct. Use 'lynxp list' or 'lynxp search' to find blueprints."));
|
|
6884
6275
|
} else if (error.statusCode === 403) {
|
|
6885
|
-
console.log(
|
|
6276
|
+
console.log(chalk12.red("\u2717 You don't have access to this blueprint."));
|
|
6886
6277
|
} else {
|
|
6887
|
-
console.log(
|
|
6278
|
+
console.log(chalk12.red(`\u2717 Error: ${error.message}`));
|
|
6888
6279
|
}
|
|
6889
6280
|
} else {
|
|
6890
|
-
console.log(
|
|
6281
|
+
console.log(chalk12.red("\u2717 An unexpected error occurred."));
|
|
6891
6282
|
}
|
|
6892
6283
|
}
|
|
6893
6284
|
}
|
|
6894
6285
|
async function unlinkCommand(fileArg) {
|
|
6895
6286
|
const cwd = process.cwd();
|
|
6896
6287
|
console.log();
|
|
6897
|
-
console.log(
|
|
6288
|
+
console.log(chalk12.cyan("\u{1F431} Unlink File from Blueprint"));
|
|
6898
6289
|
console.log();
|
|
6899
6290
|
let file;
|
|
6900
6291
|
if (!fileArg) {
|
|
6901
6292
|
const status = await checkSyncStatus(cwd);
|
|
6902
6293
|
if (status.length === 0) {
|
|
6903
|
-
console.log(
|
|
6294
|
+
console.log(chalk12.yellow("No files are currently linked to blueprints."));
|
|
6904
6295
|
return;
|
|
6905
6296
|
}
|
|
6906
|
-
const { selectedFile } = await
|
|
6297
|
+
const { selectedFile } = await prompts4({
|
|
6907
6298
|
type: "select",
|
|
6908
6299
|
name: "selectedFile",
|
|
6909
6300
|
message: "Which file do you want to unlink?",
|
|
@@ -6914,7 +6305,7 @@ async function unlinkCommand(fileArg) {
|
|
|
6914
6305
|
}))
|
|
6915
6306
|
});
|
|
6916
6307
|
if (!selectedFile) {
|
|
6917
|
-
console.log(
|
|
6308
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6918
6309
|
return;
|
|
6919
6310
|
}
|
|
6920
6311
|
file = selectedFile;
|
|
@@ -6923,77 +6314,77 @@ async function unlinkCommand(fileArg) {
|
|
|
6923
6314
|
}
|
|
6924
6315
|
const tracked = await findBlueprintByFile(cwd, file);
|
|
6925
6316
|
if (!tracked) {
|
|
6926
|
-
console.log(
|
|
6317
|
+
console.log(chalk12.yellow(`File is not linked to any blueprint: ${file}`));
|
|
6927
6318
|
return;
|
|
6928
6319
|
}
|
|
6929
|
-
console.log(
|
|
6930
|
-
console.log(
|
|
6931
|
-
console.log(
|
|
6320
|
+
console.log(chalk12.gray(`Currently linked to: ${tracked.name}`));
|
|
6321
|
+
console.log(chalk12.gray(` ID: ${tracked.id}`));
|
|
6322
|
+
console.log(chalk12.gray(` Source: ${tracked.source}`));
|
|
6932
6323
|
console.log();
|
|
6933
|
-
const { confirm } = await
|
|
6324
|
+
const { confirm } = await prompts4({
|
|
6934
6325
|
type: "confirm",
|
|
6935
6326
|
name: "confirm",
|
|
6936
|
-
message: `Unlink ${
|
|
6327
|
+
message: `Unlink ${chalk12.cyan(file)} from ${chalk12.cyan(tracked.name)}?`,
|
|
6937
6328
|
initial: true
|
|
6938
6329
|
});
|
|
6939
6330
|
if (!confirm) {
|
|
6940
|
-
console.log(
|
|
6331
|
+
console.log(chalk12.gray("Cancelled."));
|
|
6941
6332
|
return;
|
|
6942
6333
|
}
|
|
6943
6334
|
const success = await untrackBlueprint(cwd, file);
|
|
6944
6335
|
if (success) {
|
|
6945
6336
|
console.log();
|
|
6946
|
-
console.log(
|
|
6947
|
-
console.log(
|
|
6337
|
+
console.log(chalk12.green(`\u2705 Unlinked: ${file}`));
|
|
6338
|
+
console.log(chalk12.gray(" The file is now standalone. Changes won't sync with the cloud."));
|
|
6948
6339
|
console.log();
|
|
6949
6340
|
} else {
|
|
6950
|
-
console.log(
|
|
6341
|
+
console.log(chalk12.red("\u2717 Failed to unlink file."));
|
|
6951
6342
|
}
|
|
6952
6343
|
}
|
|
6953
6344
|
async function listTrackedBlueprints(cwd) {
|
|
6954
6345
|
console.log();
|
|
6955
|
-
console.log(
|
|
6346
|
+
console.log(chalk12.cyan("\u{1F431} Tracked Blueprints"));
|
|
6956
6347
|
console.log();
|
|
6957
6348
|
const status = await checkSyncStatus(cwd);
|
|
6958
6349
|
if (status.length === 0) {
|
|
6959
|
-
console.log(
|
|
6350
|
+
console.log(chalk12.gray("No blueprints are currently tracked."));
|
|
6960
6351
|
console.log();
|
|
6961
|
-
console.log(
|
|
6962
|
-
console.log(
|
|
6963
|
-
console.log(
|
|
6352
|
+
console.log(chalk12.gray("To track a blueprint:"));
|
|
6353
|
+
console.log(chalk12.gray(" lynxp pull <blueprint-id> Download and track a blueprint"));
|
|
6354
|
+
console.log(chalk12.gray(" lynxp link Link an existing file to a blueprint"));
|
|
6964
6355
|
return;
|
|
6965
6356
|
}
|
|
6966
6357
|
for (const { blueprint, localModified, fileExists: fileExists2 } of status) {
|
|
6967
|
-
const statusIcon = !fileExists2 ?
|
|
6358
|
+
const statusIcon = !fileExists2 ? chalk12.red("\u2717") : localModified ? chalk12.yellow("\u25CF") : chalk12.green("\u2713");
|
|
6968
6359
|
const sourceLabel = {
|
|
6969
|
-
marketplace:
|
|
6970
|
-
team:
|
|
6971
|
-
private:
|
|
6972
|
-
local:
|
|
6360
|
+
marketplace: chalk12.gray("[marketplace]"),
|
|
6361
|
+
team: chalk12.blue("[team]"),
|
|
6362
|
+
private: chalk12.green("[private]"),
|
|
6363
|
+
local: chalk12.gray("[local]")
|
|
6973
6364
|
}[blueprint.source];
|
|
6974
|
-
console.log(`${statusIcon} ${
|
|
6365
|
+
console.log(`${statusIcon} ${chalk12.cyan(blueprint.file)}`);
|
|
6975
6366
|
console.log(` ${sourceLabel} ${blueprint.name}`);
|
|
6976
|
-
console.log(` ${
|
|
6367
|
+
console.log(` ${chalk12.gray(`ID: ${blueprint.id}`)}`);
|
|
6977
6368
|
if (!fileExists2) {
|
|
6978
|
-
console.log(
|
|
6369
|
+
console.log(chalk12.red(` \u26A0 File not found`));
|
|
6979
6370
|
} else if (localModified) {
|
|
6980
|
-
console.log(
|
|
6371
|
+
console.log(chalk12.yellow(` \u26A0 Local changes detected`));
|
|
6981
6372
|
}
|
|
6982
6373
|
console.log();
|
|
6983
6374
|
}
|
|
6984
|
-
console.log(
|
|
6985
|
-
console.log(
|
|
6375
|
+
console.log(chalk12.gray("Legend:"));
|
|
6376
|
+
console.log(chalk12.gray(` ${chalk12.green("\u2713")} In sync ${chalk12.yellow("\u25CF")} Modified locally ${chalk12.red("\u2717")} Missing`));
|
|
6986
6377
|
console.log();
|
|
6987
6378
|
}
|
|
6988
6379
|
|
|
6989
6380
|
// src/commands/analyze.ts
|
|
6990
|
-
import
|
|
6381
|
+
import chalk13 from "chalk";
|
|
6991
6382
|
import ora11 from "ora";
|
|
6992
6383
|
async function analyzeCommand(options) {
|
|
6993
6384
|
console.log();
|
|
6994
6385
|
if (!options.json) {
|
|
6995
|
-
console.log(
|
|
6996
|
-
console.log(
|
|
6386
|
+
console.log(chalk13.cyan.bold(" \u{1F50D} LynxPrompt Analyzer"));
|
|
6387
|
+
console.log(chalk13.gray(" Detect project configuration and tech stack"));
|
|
6997
6388
|
console.log();
|
|
6998
6389
|
}
|
|
6999
6390
|
let detected;
|
|
@@ -7001,7 +6392,7 @@ async function analyzeCommand(options) {
|
|
|
7001
6392
|
if (options.remote) {
|
|
7002
6393
|
if (!isGitUrl(options.remote)) {
|
|
7003
6394
|
if (!options.json) {
|
|
7004
|
-
console.log(
|
|
6395
|
+
console.log(chalk13.red(" \u2717 Invalid Git URL provided"));
|
|
7005
6396
|
}
|
|
7006
6397
|
process.exit(1);
|
|
7007
6398
|
}
|
|
@@ -7014,7 +6405,7 @@ async function analyzeCommand(options) {
|
|
|
7014
6405
|
spinner.succeed("Remote repository analyzed");
|
|
7015
6406
|
} else {
|
|
7016
6407
|
spinner.fail("Could not analyze repository");
|
|
7017
|
-
console.log(
|
|
6408
|
+
console.log(chalk13.gray(" The repository may be private or inaccessible."));
|
|
7018
6409
|
process.exit(1);
|
|
7019
6410
|
}
|
|
7020
6411
|
} else {
|
|
@@ -7047,57 +6438,57 @@ async function analyzeCommand(options) {
|
|
|
7047
6438
|
}
|
|
7048
6439
|
console.log();
|
|
7049
6440
|
if (!detected) {
|
|
7050
|
-
console.log(
|
|
6441
|
+
console.log(chalk13.yellow(" No project configuration detected."));
|
|
7051
6442
|
console.log();
|
|
7052
|
-
console.log(
|
|
7053
|
-
console.log(
|
|
7054
|
-
console.log(
|
|
6443
|
+
console.log(chalk13.gray(" Tips:"));
|
|
6444
|
+
console.log(chalk13.gray(" \u2022 Make sure you're in a project directory with a package.json, pyproject.toml, etc."));
|
|
6445
|
+
console.log(chalk13.gray(" \u2022 Use --remote <url> to analyze a remote repository"));
|
|
7055
6446
|
console.log();
|
|
7056
6447
|
return;
|
|
7057
6448
|
}
|
|
7058
|
-
console.log(
|
|
6449
|
+
console.log(chalk13.green.bold(" \u{1F4CA} Project Analysis"));
|
|
7059
6450
|
console.log();
|
|
7060
|
-
console.log(
|
|
7061
|
-
console.log(
|
|
7062
|
-
console.log(` ${
|
|
6451
|
+
console.log(chalk13.white(" Basic Info"));
|
|
6452
|
+
console.log(chalk13.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"));
|
|
6453
|
+
console.log(` ${chalk13.dim("Name:")} ${chalk13.white(detected.name || "unknown")}`);
|
|
7063
6454
|
if (detected.description) {
|
|
7064
|
-
console.log(` ${
|
|
6455
|
+
console.log(` ${chalk13.dim("Description:")} ${chalk13.gray(detected.description)}`);
|
|
7065
6456
|
}
|
|
7066
|
-
console.log(` ${
|
|
6457
|
+
console.log(` ${chalk13.dim("Type:")} ${chalk13.white(detected.type || "application")}`);
|
|
7067
6458
|
if (detected.license) {
|
|
7068
|
-
console.log(` ${
|
|
6459
|
+
console.log(` ${chalk13.dim("License:")} ${chalk13.white(detected.license.toUpperCase())}`);
|
|
7069
6460
|
}
|
|
7070
6461
|
console.log();
|
|
7071
|
-
console.log(
|
|
7072
|
-
console.log(
|
|
6462
|
+
console.log(chalk13.white(" Tech Stack"));
|
|
6463
|
+
console.log(chalk13.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"));
|
|
7073
6464
|
if (detected.stack.length > 0) {
|
|
7074
6465
|
for (const tech of detected.stack) {
|
|
7075
|
-
console.log(` ${
|
|
6466
|
+
console.log(` ${chalk13.cyan("\u2022")} ${tech}`);
|
|
7076
6467
|
}
|
|
7077
6468
|
} else {
|
|
7078
|
-
console.log(
|
|
6469
|
+
console.log(chalk13.gray(" No tech stack detected"));
|
|
7079
6470
|
}
|
|
7080
6471
|
console.log();
|
|
7081
|
-
console.log(
|
|
7082
|
-
console.log(
|
|
7083
|
-
console.log(` ${
|
|
7084
|
-
console.log(` ${
|
|
7085
|
-
console.log(` ${
|
|
7086
|
-
console.log(` ${
|
|
6472
|
+
console.log(chalk13.white(" Infrastructure"));
|
|
6473
|
+
console.log(chalk13.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"));
|
|
6474
|
+
console.log(` ${chalk13.dim("Package Manager:")} ${detected.packageManager || chalk13.gray("none detected")}`);
|
|
6475
|
+
console.log(` ${chalk13.dim("Repo Host:")} ${detected.repoHost || chalk13.gray("none detected")}`);
|
|
6476
|
+
console.log(` ${chalk13.dim("CI/CD:")} ${detected.cicd || chalk13.gray("none detected")}`);
|
|
6477
|
+
console.log(` ${chalk13.dim("Docker:")} ${detected.hasDocker ? chalk13.green("yes") : chalk13.gray("no")}`);
|
|
7087
6478
|
console.log();
|
|
7088
6479
|
if (detected.commands && Object.keys(detected.commands).length > 0) {
|
|
7089
|
-
console.log(
|
|
7090
|
-
console.log(
|
|
6480
|
+
console.log(chalk13.white(" Commands"));
|
|
6481
|
+
console.log(chalk13.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"));
|
|
7091
6482
|
for (const [key, value] of Object.entries(detected.commands)) {
|
|
7092
6483
|
if (value) {
|
|
7093
6484
|
const cmdValue = Array.isArray(value) ? value.join(", ") : value;
|
|
7094
|
-
console.log(` ${
|
|
6485
|
+
console.log(` ${chalk13.dim(key.padEnd(10))} ${chalk13.yellow(cmdValue)}`);
|
|
7095
6486
|
}
|
|
7096
6487
|
}
|
|
7097
6488
|
console.log();
|
|
7098
6489
|
}
|
|
7099
|
-
console.log(
|
|
7100
|
-
console.log(
|
|
6490
|
+
console.log(chalk13.white(" \u{1F4A1} Recommendations"));
|
|
6491
|
+
console.log(chalk13.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"));
|
|
7101
6492
|
const recommendations = [];
|
|
7102
6493
|
if (!detected.cicd) {
|
|
7103
6494
|
recommendations.push("Add CI/CD configuration (GitHub Actions, GitLab CI, etc.)");
|
|
@@ -7116,20 +6507,20 @@ async function analyzeCommand(options) {
|
|
|
7116
6507
|
}
|
|
7117
6508
|
if (recommendations.length > 0) {
|
|
7118
6509
|
for (const rec of recommendations) {
|
|
7119
|
-
console.log(` ${
|
|
6510
|
+
console.log(` ${chalk13.yellow("\u2022")} ${rec}`);
|
|
7120
6511
|
}
|
|
7121
6512
|
} else {
|
|
7122
|
-
console.log(
|
|
6513
|
+
console.log(chalk13.green(" \u2713 Project looks well-configured!"));
|
|
7123
6514
|
}
|
|
7124
6515
|
console.log();
|
|
7125
|
-
console.log(
|
|
6516
|
+
console.log(chalk13.gray(" Run 'lynxp wizard' to generate AI IDE configuration"));
|
|
7126
6517
|
console.log();
|
|
7127
6518
|
}
|
|
7128
6519
|
|
|
7129
6520
|
// src/commands/convert.ts
|
|
7130
|
-
import
|
|
7131
|
-
import { readFile as
|
|
7132
|
-
import { join as
|
|
6521
|
+
import chalk14 from "chalk";
|
|
6522
|
+
import { readFile as readFile8, writeFile as writeFile4, access as access5 } from "fs/promises";
|
|
6523
|
+
import { join as join9, basename } from "path";
|
|
7133
6524
|
import ora12 from "ora";
|
|
7134
6525
|
var SOURCE_FILES = {
|
|
7135
6526
|
"agents.md": "agents",
|
|
@@ -7173,7 +6564,7 @@ var PLATFORM_NAMES = {
|
|
|
7173
6564
|
async function detectSourceFile(cwd) {
|
|
7174
6565
|
for (const [pattern, platform2] of Object.entries(SOURCE_FILES)) {
|
|
7175
6566
|
try {
|
|
7176
|
-
const fullPath =
|
|
6567
|
+
const fullPath = join9(cwd, pattern);
|
|
7177
6568
|
await access5(fullPath);
|
|
7178
6569
|
return { path: fullPath, platform: platform2 };
|
|
7179
6570
|
} catch {
|
|
@@ -7182,7 +6573,7 @@ async function detectSourceFile(cwd) {
|
|
|
7182
6573
|
const uppercaseVariants = ["AGENTS.md", "CLAUDE.md"];
|
|
7183
6574
|
for (const variant of uppercaseVariants) {
|
|
7184
6575
|
try {
|
|
7185
|
-
const fullPath =
|
|
6576
|
+
const fullPath = join9(cwd, variant);
|
|
7186
6577
|
await access5(fullPath);
|
|
7187
6578
|
const platform2 = variant.toLowerCase().replace(".md", "");
|
|
7188
6579
|
return { path: fullPath, platform: platform2 };
|
|
@@ -7248,28 +6639,28 @@ ${rawContent}
|
|
|
7248
6639
|
}
|
|
7249
6640
|
async function convertCommand(source, target, options) {
|
|
7250
6641
|
console.log();
|
|
7251
|
-
console.log(
|
|
7252
|
-
console.log(
|
|
6642
|
+
console.log(chalk14.cyan.bold(" \u{1F504} LynxPrompt Convert"));
|
|
6643
|
+
console.log(chalk14.gray(" Convert AI IDE configuration between formats"));
|
|
7253
6644
|
console.log();
|
|
7254
6645
|
const cwd = process.cwd();
|
|
7255
6646
|
let sourcePath;
|
|
7256
6647
|
let sourcePlatform;
|
|
7257
6648
|
if (source) {
|
|
7258
|
-
sourcePath =
|
|
6649
|
+
sourcePath = join9(cwd, source);
|
|
7259
6650
|
const sourceBasename = basename(source).toLowerCase();
|
|
7260
6651
|
sourcePlatform = SOURCE_FILES[sourceBasename] || "unknown";
|
|
7261
6652
|
} else {
|
|
7262
6653
|
const detected = await detectSourceFile(cwd);
|
|
7263
6654
|
if (!detected) {
|
|
7264
|
-
console.log(
|
|
6655
|
+
console.log(chalk14.red(" \u2717 No AI configuration file found in current directory"));
|
|
7265
6656
|
console.log();
|
|
7266
|
-
console.log(
|
|
6657
|
+
console.log(chalk14.gray(" Supported source files:"));
|
|
7267
6658
|
for (const file of Object.keys(SOURCE_FILES)) {
|
|
7268
|
-
console.log(
|
|
6659
|
+
console.log(chalk14.gray(` \u2022 ${file}`));
|
|
7269
6660
|
}
|
|
7270
6661
|
console.log();
|
|
7271
|
-
console.log(
|
|
7272
|
-
console.log(
|
|
6662
|
+
console.log(chalk14.gray(" Usage: lynxp convert <source> <target>"));
|
|
6663
|
+
console.log(chalk14.gray(" Example: lynxp convert AGENTS.md cursor"));
|
|
7273
6664
|
process.exit(1);
|
|
7274
6665
|
}
|
|
7275
6666
|
sourcePath = detected.path;
|
|
@@ -7277,63 +6668,63 @@ async function convertCommand(source, target, options) {
|
|
|
7277
6668
|
}
|
|
7278
6669
|
const normalizedTarget = target.toLowerCase().replace("-", "_");
|
|
7279
6670
|
if (!TARGET_FILES[normalizedTarget]) {
|
|
7280
|
-
console.log(
|
|
6671
|
+
console.log(chalk14.red(` \u2717 Unknown target format: ${target}`));
|
|
7281
6672
|
console.log();
|
|
7282
|
-
console.log(
|
|
6673
|
+
console.log(chalk14.gray(" Supported target formats:"));
|
|
7283
6674
|
for (const [key, name] of Object.entries(PLATFORM_NAMES)) {
|
|
7284
|
-
console.log(
|
|
6675
|
+
console.log(chalk14.gray(` \u2022 ${key.padEnd(15)} \u2192 ${name}`));
|
|
7285
6676
|
}
|
|
7286
6677
|
process.exit(1);
|
|
7287
6678
|
}
|
|
7288
6679
|
let sourceContent;
|
|
7289
6680
|
try {
|
|
7290
|
-
sourceContent = await
|
|
6681
|
+
sourceContent = await readFile8(sourcePath, "utf-8");
|
|
7291
6682
|
} catch {
|
|
7292
|
-
console.log(
|
|
6683
|
+
console.log(chalk14.red(` \u2717 Could not read source file: ${sourcePath}`));
|
|
7293
6684
|
process.exit(1);
|
|
7294
6685
|
}
|
|
7295
|
-
console.log(
|
|
7296
|
-
console.log(
|
|
6686
|
+
console.log(chalk14.white(` Source: ${basename(sourcePath)} (${PLATFORM_NAMES[sourcePlatform] || sourcePlatform})`));
|
|
6687
|
+
console.log(chalk14.white(` Target: ${PLATFORM_NAMES[normalizedTarget]}`));
|
|
7297
6688
|
console.log();
|
|
7298
6689
|
const spinner = ora12("Converting configuration...").start();
|
|
7299
6690
|
const config2 = parseMarkdownConfig(sourceContent);
|
|
7300
6691
|
const targetContent = generateTargetContent(config2, normalizedTarget);
|
|
7301
6692
|
spinner.stop();
|
|
7302
6693
|
const outputFilename = options.output || TARGET_FILES[normalizedTarget];
|
|
7303
|
-
const outputPath =
|
|
6694
|
+
const outputPath = join9(cwd, outputFilename);
|
|
7304
6695
|
try {
|
|
7305
6696
|
await access5(outputPath);
|
|
7306
6697
|
if (!options.force) {
|
|
7307
|
-
console.log(
|
|
7308
|
-
console.log(
|
|
6698
|
+
console.log(chalk14.yellow(` \u26A0\uFE0F File already exists: ${outputFilename}`));
|
|
6699
|
+
console.log(chalk14.gray(" Use --force to overwrite"));
|
|
7309
6700
|
process.exit(1);
|
|
7310
6701
|
}
|
|
7311
6702
|
} catch {
|
|
7312
6703
|
}
|
|
7313
6704
|
try {
|
|
7314
6705
|
const { mkdir: mkdir4 } = await import("fs/promises");
|
|
7315
|
-
const outputDir =
|
|
6706
|
+
const outputDir = join9(cwd, outputFilename.split("/").slice(0, -1).join("/"));
|
|
7316
6707
|
if (outputDir !== cwd) {
|
|
7317
6708
|
await mkdir4(outputDir, { recursive: true });
|
|
7318
6709
|
}
|
|
7319
|
-
await
|
|
7320
|
-
console.log(
|
|
6710
|
+
await writeFile4(outputPath, targetContent, "utf-8");
|
|
6711
|
+
console.log(chalk14.green(` \u2713 Converted to ${outputFilename}`));
|
|
7321
6712
|
console.log();
|
|
7322
|
-
console.log(
|
|
7323
|
-
console.log(
|
|
6713
|
+
console.log(chalk14.gray(` Lines: ${targetContent.split("\n").length}`));
|
|
6714
|
+
console.log(chalk14.gray(` Size: ${targetContent.length} bytes`));
|
|
7324
6715
|
console.log();
|
|
7325
6716
|
} catch (error) {
|
|
7326
|
-
console.log(
|
|
6717
|
+
console.log(chalk14.red(` \u2717 Could not write output: ${error instanceof Error ? error.message : "unknown error"}`));
|
|
7327
6718
|
process.exit(1);
|
|
7328
6719
|
}
|
|
7329
6720
|
}
|
|
7330
6721
|
|
|
7331
6722
|
// src/commands/merge.ts
|
|
7332
|
-
import
|
|
7333
|
-
import { readFile as
|
|
7334
|
-
import { join as
|
|
6723
|
+
import chalk15 from "chalk";
|
|
6724
|
+
import { readFile as readFile9, writeFile as writeFile5, access as access6 } from "fs/promises";
|
|
6725
|
+
import { join as join10, basename as basename2 } from "path";
|
|
7335
6726
|
import ora13 from "ora";
|
|
7336
|
-
import
|
|
6727
|
+
import prompts5 from "prompts";
|
|
7337
6728
|
function parseMarkdownSections(content, sourceName) {
|
|
7338
6729
|
const sections = [];
|
|
7339
6730
|
const parts = content.split(/^(#{1,3})\s+(.+)$/m);
|
|
@@ -7433,43 +6824,43 @@ ${section.content.trim()}`);
|
|
|
7433
6824
|
}
|
|
7434
6825
|
async function mergeCommand(files, options) {
|
|
7435
6826
|
console.log();
|
|
7436
|
-
console.log(
|
|
7437
|
-
console.log(
|
|
6827
|
+
console.log(chalk15.cyan.bold(" \u{1F500} LynxPrompt Merge"));
|
|
6828
|
+
console.log(chalk15.gray(" Merge multiple AI IDE configuration files"));
|
|
7438
6829
|
console.log();
|
|
7439
6830
|
if (files.length < 2) {
|
|
7440
|
-
console.log(
|
|
6831
|
+
console.log(chalk15.red(" \u2717 Please provide at least 2 files to merge"));
|
|
7441
6832
|
console.log();
|
|
7442
|
-
console.log(
|
|
7443
|
-
console.log(
|
|
6833
|
+
console.log(chalk15.gray(" Usage: lynxp merge <file1> <file2> [...files]"));
|
|
6834
|
+
console.log(chalk15.gray(" Example: lynxp merge AGENTS.md team-rules.md --output merged.md"));
|
|
7444
6835
|
console.log();
|
|
7445
|
-
console.log(
|
|
7446
|
-
console.log(
|
|
7447
|
-
console.log(
|
|
7448
|
-
console.log(
|
|
7449
|
-
console.log(
|
|
6836
|
+
console.log(chalk15.gray(" Options:"));
|
|
6837
|
+
console.log(chalk15.gray(" --output <file> Output filename (default: merged.md)"));
|
|
6838
|
+
console.log(chalk15.gray(" --strategy <type> Merge strategy: concat, sections, smart (default: smart)"));
|
|
6839
|
+
console.log(chalk15.gray(" --force Overwrite existing output file"));
|
|
6840
|
+
console.log(chalk15.gray(" --interactive Review and select sections to include"));
|
|
7450
6841
|
process.exit(1);
|
|
7451
6842
|
}
|
|
7452
6843
|
const cwd = process.cwd();
|
|
7453
6844
|
const strategy = options.strategy || "smart";
|
|
7454
|
-
console.log(
|
|
7455
|
-
console.log(
|
|
6845
|
+
console.log(chalk15.white(` Files to merge: ${files.length}`));
|
|
6846
|
+
console.log(chalk15.white(` Strategy: ${strategy}`));
|
|
7456
6847
|
console.log();
|
|
7457
6848
|
const allSections = [];
|
|
7458
6849
|
for (const file of files) {
|
|
7459
|
-
const filePath =
|
|
6850
|
+
const filePath = join10(cwd, file);
|
|
7460
6851
|
try {
|
|
7461
6852
|
await access6(filePath);
|
|
7462
6853
|
} catch {
|
|
7463
|
-
console.log(
|
|
6854
|
+
console.log(chalk15.red(` \u2717 File not found: ${file}`));
|
|
7464
6855
|
process.exit(1);
|
|
7465
6856
|
}
|
|
7466
6857
|
try {
|
|
7467
|
-
const content = await
|
|
6858
|
+
const content = await readFile9(filePath, "utf-8");
|
|
7468
6859
|
const sections = parseMarkdownSections(content, basename2(file));
|
|
7469
6860
|
allSections.push(sections);
|
|
7470
|
-
console.log(
|
|
6861
|
+
console.log(chalk15.gray(` \u2713 Read ${file} (${sections.length} sections)`));
|
|
7471
6862
|
} catch (error) {
|
|
7472
|
-
console.log(
|
|
6863
|
+
console.log(chalk15.red(` \u2717 Could not read ${file}: ${error instanceof Error ? error.message : "unknown"}`));
|
|
7473
6864
|
process.exit(1);
|
|
7474
6865
|
}
|
|
7475
6866
|
}
|
|
@@ -7483,7 +6874,7 @@ async function mergeCommand(files, options) {
|
|
|
7483
6874
|
value: i,
|
|
7484
6875
|
selected: true
|
|
7485
6876
|
}));
|
|
7486
|
-
const response = await
|
|
6877
|
+
const response = await prompts5({
|
|
7487
6878
|
type: "multiselect",
|
|
7488
6879
|
name: "sections",
|
|
7489
6880
|
message: "Select sections to include:",
|
|
@@ -7492,7 +6883,7 @@ async function mergeCommand(files, options) {
|
|
|
7492
6883
|
hint: "- Space to toggle, Enter to confirm"
|
|
7493
6884
|
});
|
|
7494
6885
|
if (!response.sections || response.sections.length === 0) {
|
|
7495
|
-
console.log(
|
|
6886
|
+
console.log(chalk15.yellow(" No sections selected, aborting."));
|
|
7496
6887
|
process.exit(0);
|
|
7497
6888
|
}
|
|
7498
6889
|
const selectedSections = response.sections.map((i) => flatSections[i]);
|
|
@@ -7513,33 +6904,35 @@ async function mergeCommand(files, options) {
|
|
|
7513
6904
|
${mergedContent}
|
|
7514
6905
|
`;
|
|
7515
6906
|
const outputFilename = options.output || "merged.md";
|
|
7516
|
-
const outputPath =
|
|
6907
|
+
const outputPath = join10(cwd, outputFilename);
|
|
7517
6908
|
try {
|
|
7518
6909
|
await access6(outputPath);
|
|
7519
6910
|
if (!options.force) {
|
|
7520
|
-
console.log(
|
|
7521
|
-
console.log(
|
|
6911
|
+
console.log(chalk15.yellow(` \u26A0\uFE0F File already exists: ${outputFilename}`));
|
|
6912
|
+
console.log(chalk15.gray(" Use --force to overwrite"));
|
|
7522
6913
|
process.exit(1);
|
|
7523
6914
|
}
|
|
7524
6915
|
} catch {
|
|
7525
6916
|
}
|
|
7526
6917
|
try {
|
|
7527
|
-
await
|
|
7528
|
-
console.log(
|
|
6918
|
+
await writeFile5(outputPath, finalContent, "utf-8");
|
|
6919
|
+
console.log(chalk15.green(` \u2713 Merged to ${outputFilename}`));
|
|
7529
6920
|
console.log();
|
|
7530
|
-
console.log(
|
|
7531
|
-
console.log(
|
|
7532
|
-
console.log(
|
|
6921
|
+
console.log(chalk15.gray(` Sources: ${files.length} files`));
|
|
6922
|
+
console.log(chalk15.gray(` Lines: ${finalContent.split("\n").length}`));
|
|
6923
|
+
console.log(chalk15.gray(` Size: ${finalContent.length} bytes`));
|
|
7533
6924
|
console.log();
|
|
7534
6925
|
} catch (error) {
|
|
7535
|
-
console.log(
|
|
6926
|
+
console.log(chalk15.red(` \u2717 Could not write output: ${error instanceof Error ? error.message : "unknown error"}`));
|
|
7536
6927
|
process.exit(1);
|
|
7537
6928
|
}
|
|
7538
6929
|
}
|
|
7539
6930
|
|
|
7540
6931
|
// src/index.ts
|
|
6932
|
+
var require2 = createRequire(import.meta.url);
|
|
6933
|
+
var packageJson = require2("../package.json");
|
|
7541
6934
|
var program = new Command();
|
|
7542
|
-
program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version(
|
|
6935
|
+
program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version(packageJson.version);
|
|
7543
6936
|
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);
|
|
7544
6937
|
program.command("check").description("Validate AI configuration files (for CI/CD)").option("--ci", "CI mode - exit codes only (0=pass, 1=fail)").action(checkCommand);
|
|
7545
6938
|
program.command("analyze").description("Analyze project configuration without generating files").option("-r, --remote <url>", "Analyze a remote repository (GitHub/GitLab)").option("-j, --json", "Output as JSON (for scripting)").action(analyzeCommand);
|
|
@@ -7553,47 +6946,46 @@ program.command("push [file]").description("Push local file to LynxPrompt cloud
|
|
|
7553
6946
|
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);
|
|
7554
6947
|
program.command("unlink [file]").description("Disconnect a local file from its cloud blueprint").action(unlinkCommand);
|
|
7555
6948
|
program.command("diff [file-or-id]").description("Compare tracked files with their cloud blueprints").option("--local", "Compare .lynxprompt/rules/ with exported files").action(diffCommand);
|
|
7556
|
-
program.command("agents [action] [agent]").description("Manage AI agents (list, enable, disable, detect)").option("-i, --interactive", "Interactive agent selection").action(agentsCommand);
|
|
7557
6949
|
program.command("login").description("Authenticate with LynxPrompt (opens browser)").action(loginCommand);
|
|
7558
6950
|
program.command("logout").description("Log out and remove stored credentials").action(logoutCommand);
|
|
7559
6951
|
program.command("whoami").description("Show current authenticated user").action(whoamiCommand);
|
|
7560
6952
|
program.addHelpText(
|
|
7561
6953
|
"beforeAll",
|
|
7562
6954
|
`
|
|
7563
|
-
${
|
|
7564
|
-
${
|
|
6955
|
+
${chalk16.cyan("\u{1F431} LynxPrompt CLI")} ${chalk16.gray("(also available as: lynxp)")}
|
|
6956
|
+
${chalk16.gray("Generate AI IDE configuration files from your terminal")}
|
|
7565
6957
|
`
|
|
7566
6958
|
);
|
|
7567
6959
|
program.addHelpText(
|
|
7568
6960
|
"after",
|
|
7569
6961
|
`
|
|
7570
|
-
${
|
|
7571
|
-
${
|
|
7572
|
-
${
|
|
7573
|
-
${
|
|
7574
|
-
${
|
|
6962
|
+
${chalk16.cyan("Quick Start:")}
|
|
6963
|
+
${chalk16.white("$ lynxp wizard")} ${chalk16.gray("Generate config interactively")}
|
|
6964
|
+
${chalk16.white("$ lynxp wizard -y")} ${chalk16.gray("Generate AGENTS.md with defaults")}
|
|
6965
|
+
${chalk16.white("$ lynxp wizard -f cursor")} ${chalk16.gray("Generate .cursor/rules/")}
|
|
6966
|
+
${chalk16.white("$ lynxp wizard --blueprint")} ${chalk16.gray("Generate with [[VAR|default]] placeholders")}
|
|
7575
6967
|
|
|
7576
|
-
${
|
|
7577
|
-
${
|
|
7578
|
-
${
|
|
7579
|
-
${
|
|
7580
|
-
${
|
|
6968
|
+
${chalk16.cyan("Analysis & Tools:")}
|
|
6969
|
+
${chalk16.white("$ lynxp analyze")} ${chalk16.gray("Analyze project tech stack")}
|
|
6970
|
+
${chalk16.white("$ lynxp analyze -r <url>")} ${chalk16.gray("Analyze remote repository")}
|
|
6971
|
+
${chalk16.white("$ lynxp convert AGENTS.md cursor")} ${chalk16.gray("Convert to Cursor format")}
|
|
6972
|
+
${chalk16.white("$ lynxp merge a.md b.md -o out.md")} ${chalk16.gray("Merge multiple configs")}
|
|
7581
6973
|
|
|
7582
|
-
${
|
|
7583
|
-
${
|
|
7584
|
-
${
|
|
7585
|
-
${
|
|
7586
|
-
${
|
|
6974
|
+
${chalk16.cyan("Marketplace:")}
|
|
6975
|
+
${chalk16.white("$ lynxp search nextjs")} ${chalk16.gray("Search blueprints")}
|
|
6976
|
+
${chalk16.white("$ lynxp pull bp_abc123")} ${chalk16.gray("Download and track a blueprint")}
|
|
6977
|
+
${chalk16.white("$ lynxp push")} ${chalk16.gray("Push local file to cloud")}
|
|
6978
|
+
${chalk16.white("$ lynxp link --list")} ${chalk16.gray("Show tracked blueprints")}
|
|
7587
6979
|
|
|
7588
|
-
${
|
|
7589
|
-
${
|
|
7590
|
-
${
|
|
7591
|
-
${
|
|
6980
|
+
${chalk16.cyan("Blueprint Tracking:")}
|
|
6981
|
+
${chalk16.white("$ lynxp link AGENTS.md bp_xyz")} ${chalk16.gray("Link existing file to blueprint")}
|
|
6982
|
+
${chalk16.white("$ lynxp unlink AGENTS.md")} ${chalk16.gray("Disconnect from cloud")}
|
|
6983
|
+
${chalk16.white("$ lynxp diff bp_abc123")} ${chalk16.gray("Show changes vs cloud version")}
|
|
7592
6984
|
|
|
7593
|
-
${
|
|
7594
|
-
${
|
|
6985
|
+
${chalk16.cyan("CI/CD:")}
|
|
6986
|
+
${chalk16.white("$ lynxp check --ci")} ${chalk16.gray("Validate config (exit code)")}
|
|
7595
6987
|
|
|
7596
|
-
${
|
|
6988
|
+
${chalk16.gray("Docs: https://lynxprompt.com/docs/cli")}
|
|
7597
6989
|
`
|
|
7598
6990
|
);
|
|
7599
6991
|
program.parse();
|