lynxprompt 2.1.1 → 2.1.2

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 CHANGED
@@ -264,6 +264,8 @@ var ApiRequestError = class extends Error {
264
264
  this.response = response;
265
265
  this.name = "ApiRequestError";
266
266
  }
267
+ statusCode;
268
+ response;
267
269
  };
268
270
  var api = new ApiClient();
269
271
 
@@ -355,7 +357,7 @@ async function tryOpenBrowser(url) {
355
357
  }
356
358
  }
357
359
  function sleep(ms) {
358
- return new Promise((resolve) => setTimeout(resolve, ms));
360
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
359
361
  }
360
362
  function displayWelcome(user) {
361
363
  const name = user.name || user.email.split("@")[0];
@@ -534,7 +536,7 @@ import chalk5 from "chalk";
534
536
  import ora4 from "ora";
535
537
  import prompts from "prompts";
536
538
  import { writeFile as writeFile2, mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
537
- import { join as join2, dirname as dirname2 } from "path";
539
+ import { dirname as dirname2, resolve, relative } from "path";
538
540
  import { existsSync } from "fs";
539
541
 
540
542
  // src/utils/blueprint-tracker.ts
@@ -656,6 +658,14 @@ async function checkSyncStatus(cwd) {
656
658
  }
657
659
 
658
660
  // src/commands/pull.ts
661
+ function safePath(root, untrusted) {
662
+ const resolved = resolve(root, untrusted);
663
+ const rel = relative(root, resolved);
664
+ if (rel.startsWith("..") || resolve(root, rel) !== resolved) {
665
+ throw new Error(`Path traversal blocked: ${untrusted}`);
666
+ }
667
+ return resolved;
668
+ }
659
669
  var TYPE_TO_FILENAME = {
660
670
  AGENTS_MD: "AGENTS.md",
661
671
  CURSOR_RULES: ".cursor/rules/project.mdc",
@@ -739,7 +749,14 @@ async function pullHierarchy(id, options) {
739
749
  let skipped = 0;
740
750
  for (const bp of blueprints) {
741
751
  const filename = bp.repository_path || TYPE_TO_FILENAME[bp.type] || "ai-config.md";
742
- const outputPath = join2(options.output, filename);
752
+ let outputPath;
753
+ try {
754
+ outputPath = safePath(resolve(options.output), filename);
755
+ } catch {
756
+ console.log(chalk5.red(` \u2717 Skipped (invalid path): ${filename}`));
757
+ skipped++;
758
+ continue;
759
+ }
743
760
  if (existsSync(outputPath) && !options.yes) {
744
761
  const { overwrite } = await prompts({
745
762
  type: "confirm",
@@ -857,7 +874,13 @@ async function pullBlueprint(id, options) {
857
874
  return;
858
875
  }
859
876
  const filename = blueprint.repository_path || TYPE_TO_FILENAME[blueprint.type] || "ai-config.md";
860
- const outputPath = join2(options.output, filename);
877
+ let outputPath;
878
+ try {
879
+ outputPath = safePath(resolve(options.output), filename);
880
+ } catch {
881
+ console.error(chalk5.red(`\u2717 Invalid file path: ${filename}`));
882
+ process.exit(1);
883
+ }
861
884
  let localContent = null;
862
885
  if (existsSync(outputPath)) {
863
886
  try {
@@ -1727,11 +1750,11 @@ import ora6 from "ora";
1727
1750
  import * as readline from "readline";
1728
1751
  import * as os from "os";
1729
1752
  import { writeFile as writeFile3, mkdir as mkdir3, access as access3, readFile as readFile4 } from "fs/promises";
1730
- import { join as join4, dirname as dirname3 } from "path";
1753
+ import { join as join3, dirname as dirname3 } from "path";
1731
1754
 
1732
1755
  // src/utils/detect.ts
1733
1756
  import { readFile as readFile3, access as access2, rm, mkdtemp } from "fs/promises";
1734
- import { join as join3 } from "path";
1757
+ import { join as join2 } from "path";
1735
1758
  import { tmpdir } from "os";
1736
1759
  import { spawnSync } from "child_process";
1737
1760
  var JS_FRAMEWORK_PATTERNS = {
@@ -1787,7 +1810,7 @@ async function detectExtendedCommands(cwd) {
1787
1810
  cmds[category].push({ cmd, desc });
1788
1811
  }
1789
1812
  };
1790
- const packageJsonPath = join3(cwd, "package.json");
1813
+ const packageJsonPath = join2(cwd, "package.json");
1791
1814
  if (await fileExists(packageJsonPath)) {
1792
1815
  try {
1793
1816
  const content = await readFile3(packageJsonPath, "utf-8");
@@ -1826,7 +1849,7 @@ async function detectExtendedCommands(cwd) {
1826
1849
  } catch {
1827
1850
  }
1828
1851
  }
1829
- const pyprojectPath = join3(cwd, "pyproject.toml");
1852
+ const pyprojectPath = join2(cwd, "pyproject.toml");
1830
1853
  if (await fileExists(pyprojectPath)) {
1831
1854
  try {
1832
1855
  const content = await readFile3(pyprojectPath, "utf-8");
@@ -1879,7 +1902,7 @@ async function detectExtendedCommands(cwd) {
1879
1902
  }
1880
1903
  if (content.includes("[tool.poetry]")) {
1881
1904
  addCmd("install", "poetry install", "Install dependencies with Poetry");
1882
- } else if (await fileExists(join3(cwd, "uv.lock"))) {
1905
+ } else if (await fileExists(join2(cwd, "uv.lock"))) {
1883
1906
  addCmd("install", "uv sync", "Sync dependencies with uv");
1884
1907
  } else {
1885
1908
  addCmd("install", "pip install -r requirements.txt", "Install dependencies with pip");
@@ -1887,7 +1910,7 @@ async function detectExtendedCommands(cwd) {
1887
1910
  } catch {
1888
1911
  }
1889
1912
  }
1890
- const requirementsPath = join3(cwd, "requirements.txt");
1913
+ const requirementsPath = join2(cwd, "requirements.txt");
1891
1914
  if (await fileExists(requirementsPath)) {
1892
1915
  try {
1893
1916
  const content = await readFile3(requirementsPath, "utf-8");
@@ -1898,7 +1921,7 @@ async function detectExtendedCommands(cwd) {
1898
1921
  } catch {
1899
1922
  }
1900
1923
  }
1901
- const makefilePath = join3(cwd, "Makefile");
1924
+ const makefilePath = join2(cwd, "Makefile");
1902
1925
  if (await fileExists(makefilePath)) {
1903
1926
  try {
1904
1927
  const content = await readFile3(makefilePath, "utf-8");
@@ -1931,8 +1954,8 @@ async function detectExtendedCommands(cwd) {
1931
1954
  } catch {
1932
1955
  }
1933
1956
  }
1934
- const dockerComposePath = join3(cwd, "docker-compose.yml");
1935
- const dockerComposeYamlPath = join3(cwd, "docker-compose.yaml");
1957
+ const dockerComposePath = join2(cwd, "docker-compose.yml");
1958
+ const dockerComposeYamlPath = join2(cwd, "docker-compose.yaml");
1936
1959
  const composePath = await fileExists(dockerComposePath) ? dockerComposePath : await fileExists(dockerComposeYamlPath) ? dockerComposeYamlPath : null;
1937
1960
  if (composePath) {
1938
1961
  try {
@@ -1948,7 +1971,7 @@ async function detectExtendedCommands(cwd) {
1948
1971
  } catch {
1949
1972
  }
1950
1973
  }
1951
- const dockerfilePath = join3(cwd, "Dockerfile");
1974
+ const dockerfilePath = join2(cwd, "Dockerfile");
1952
1975
  if (await fileExists(dockerfilePath)) {
1953
1976
  try {
1954
1977
  const content = await readFile3(dockerfilePath, "utf-8");
@@ -1960,13 +1983,13 @@ async function detectExtendedCommands(cwd) {
1960
1983
  }
1961
1984
  }
1962
1985
  try {
1963
- const dockerViewerPath = join3(cwd, "Dockerfile.viewer");
1986
+ const dockerViewerPath = join2(cwd, "Dockerfile.viewer");
1964
1987
  if (await fileExists(dockerViewerPath)) {
1965
1988
  addCmd("build", "docker build -f Dockerfile.viewer -t app-viewer .", "Build viewer Docker image");
1966
1989
  }
1967
1990
  } catch {
1968
1991
  }
1969
- const cargoPath = join3(cwd, "Cargo.toml");
1992
+ const cargoPath = join2(cwd, "Cargo.toml");
1970
1993
  if (await fileExists(cargoPath)) {
1971
1994
  addCmd("build", "cargo build", "Build Rust project");
1972
1995
  addCmd("build", "cargo build --release", "Build release");
@@ -1976,7 +1999,7 @@ async function detectExtendedCommands(cwd) {
1976
1999
  addCmd("dev", "cargo run", "Run Rust binary");
1977
2000
  addCmd("clean", "cargo clean", "Clean build artifacts");
1978
2001
  }
1979
- const goModPath = join3(cwd, "go.mod");
2002
+ const goModPath = join2(cwd, "go.mod");
1980
2003
  if (await fileExists(goModPath)) {
1981
2004
  addCmd("build", "go build", "Build Go project");
1982
2005
  addCmd("test", "go test ./...", "Run Go tests");
@@ -1986,9 +2009,9 @@ async function detectExtendedCommands(cwd) {
1986
2009
  addCmd("clean", "go clean", "Clean build cache");
1987
2010
  addCmd("typecheck", "go vet ./...", "Run go vet");
1988
2011
  }
1989
- const srcMainPath = join3(cwd, "src", "main.py");
1990
- const mainPath = join3(cwd, "main.py");
1991
- const appPath = join3(cwd, "app.py");
2012
+ const srcMainPath = join2(cwd, "src", "main.py");
2013
+ const mainPath = join2(cwd, "main.py");
2014
+ const appPath = join2(cwd, "app.py");
1992
2015
  if (await fileExists(srcMainPath)) {
1993
2016
  addCmd("dev", "python -m src.main", "Run main module");
1994
2017
  }
@@ -1998,15 +2021,15 @@ async function detectExtendedCommands(cwd) {
1998
2021
  if (await fileExists(appPath)) {
1999
2022
  addCmd("dev", "python app.py", "Run app.py");
2000
2023
  }
2001
- const schedulerPath = join3(cwd, "src", "scheduler.py");
2024
+ const schedulerPath = join2(cwd, "src", "scheduler.py");
2002
2025
  if (await fileExists(schedulerPath)) {
2003
2026
  addCmd("additional", "python -m src.scheduler", "Run scheduler");
2004
2027
  }
2005
- const setupAuthPath = join3(cwd, "src", "setup_auth.py");
2028
+ const setupAuthPath = join2(cwd, "src", "setup_auth.py");
2006
2029
  if (await fileExists(setupAuthPath)) {
2007
2030
  addCmd("additional", "python -m src.setup_auth", "Setup authentication");
2008
2031
  }
2009
- const webMainPath = join3(cwd, "src", "web", "main.py");
2032
+ const webMainPath = join2(cwd, "src", "web", "main.py");
2010
2033
  if (await fileExists(webMainPath)) {
2011
2034
  addCmd("dev", "uvicorn src.web.main:app --host 0.0.0.0 --port 8080", "Run web viewer");
2012
2035
  }
@@ -2021,14 +2044,14 @@ async function detectProject(cwd) {
2021
2044
  packageManager: null,
2022
2045
  type: "unknown"
2023
2046
  };
2024
- const packageJsonPath = join3(cwd, "package.json");
2047
+ const packageJsonPath = join2(cwd, "package.json");
2025
2048
  if (await fileExists(packageJsonPath)) {
2026
2049
  try {
2027
2050
  const content = await readFile3(packageJsonPath, "utf-8");
2028
2051
  const pkg = JSON.parse(content);
2029
2052
  detected.name = pkg.name || null;
2030
2053
  detected.description = pkg.description;
2031
- if (pkg.workspaces || await fileExists(join3(cwd, "pnpm-workspace.yaml"))) {
2054
+ if (pkg.workspaces || await fileExists(join2(cwd, "pnpm-workspace.yaml"))) {
2032
2055
  detected.type = "monorepo";
2033
2056
  } else if (pkg.main || pkg.exports) {
2034
2057
  detected.type = "library";
@@ -2056,13 +2079,13 @@ async function detectProject(cwd) {
2056
2079
  detected.commands.dev = pkg.scripts.dev || pkg.scripts.start || pkg.scripts.serve;
2057
2080
  detected.commands.format = pkg.scripts.format || pkg.scripts.prettier;
2058
2081
  }
2059
- if (await fileExists(join3(cwd, "pnpm-lock.yaml"))) {
2082
+ if (await fileExists(join2(cwd, "pnpm-lock.yaml"))) {
2060
2083
  detected.packageManager = "pnpm";
2061
- } else if (await fileExists(join3(cwd, "yarn.lock"))) {
2084
+ } else if (await fileExists(join2(cwd, "yarn.lock"))) {
2062
2085
  detected.packageManager = "yarn";
2063
- } else if (await fileExists(join3(cwd, "bun.lockb"))) {
2086
+ } else if (await fileExists(join2(cwd, "bun.lockb"))) {
2064
2087
  detected.packageManager = "bun";
2065
- } else if (await fileExists(join3(cwd, "package-lock.json"))) {
2088
+ } else if (await fileExists(join2(cwd, "package-lock.json"))) {
2066
2089
  detected.packageManager = "npm";
2067
2090
  }
2068
2091
  if (detected.packageManager && detected.packageManager !== "npm") {
@@ -2089,7 +2112,7 @@ async function detectProject(cwd) {
2089
2112
  } catch {
2090
2113
  }
2091
2114
  }
2092
- const pyprojectPath = join3(cwd, "pyproject.toml");
2115
+ const pyprojectPath = join2(cwd, "pyproject.toml");
2093
2116
  if (await fileExists(pyprojectPath)) {
2094
2117
  try {
2095
2118
  const content = await readFile3(pyprojectPath, "utf-8");
@@ -2110,14 +2133,14 @@ async function detectProject(cwd) {
2110
2133
  if (content.includes("[tool.poetry]")) {
2111
2134
  detected.packageManager = "yarn";
2112
2135
  detected.commands.dev = "poetry run python -m uvicorn main:app --reload";
2113
- } else if (await fileExists(join3(cwd, "uv.lock"))) {
2136
+ } else if (await fileExists(join2(cwd, "uv.lock"))) {
2114
2137
  detected.commands.dev = "uv run python main.py";
2115
2138
  }
2116
2139
  return detected;
2117
2140
  } catch {
2118
2141
  }
2119
2142
  }
2120
- const requirementsPath = join3(cwd, "requirements.txt");
2143
+ const requirementsPath = join2(cwd, "requirements.txt");
2121
2144
  if (await fileExists(requirementsPath)) {
2122
2145
  try {
2123
2146
  const content = await readFile3(requirementsPath, "utf-8");
@@ -2132,7 +2155,7 @@ async function detectProject(cwd) {
2132
2155
  } catch {
2133
2156
  }
2134
2157
  }
2135
- const cargoPath = join3(cwd, "Cargo.toml");
2158
+ const cargoPath = join2(cwd, "Cargo.toml");
2136
2159
  if (await fileExists(cargoPath)) {
2137
2160
  try {
2138
2161
  const content = await readFile3(cargoPath, "utf-8");
@@ -2153,7 +2176,7 @@ async function detectProject(cwd) {
2153
2176
  } catch {
2154
2177
  }
2155
2178
  }
2156
- const goModPath = join3(cwd, "go.mod");
2179
+ const goModPath = join2(cwd, "go.mod");
2157
2180
  if (await fileExists(goModPath)) {
2158
2181
  try {
2159
2182
  const content = await readFile3(goModPath, "utf-8");
@@ -2176,7 +2199,7 @@ async function detectProject(cwd) {
2176
2199
  } catch {
2177
2200
  }
2178
2201
  }
2179
- const makefilePath = join3(cwd, "Makefile");
2202
+ const makefilePath = join2(cwd, "Makefile");
2180
2203
  if (await fileExists(makefilePath)) {
2181
2204
  try {
2182
2205
  const content = await readFile3(makefilePath, "utf-8");
@@ -2192,12 +2215,12 @@ async function detectProject(cwd) {
2192
2215
  } catch {
2193
2216
  }
2194
2217
  }
2195
- if (await fileExists(join3(cwd, "Dockerfile")) || await fileExists(join3(cwd, "docker-compose.yml"))) {
2218
+ if (await fileExists(join2(cwd, "Dockerfile")) || await fileExists(join2(cwd, "docker-compose.yml"))) {
2196
2219
  detected.stack.push("docker");
2197
2220
  detected.type = "application";
2198
2221
  detected.hasDocker = true;
2199
2222
  }
2200
- const licensePath = join3(cwd, "LICENSE");
2223
+ const licensePath = join2(cwd, "LICENSE");
2201
2224
  if (await fileExists(licensePath)) {
2202
2225
  try {
2203
2226
  const licenseContent = await readFile3(licensePath, "utf-8");
@@ -2222,7 +2245,7 @@ async function detectProject(cwd) {
2222
2245
  } catch {
2223
2246
  }
2224
2247
  }
2225
- const gitConfigPath = join3(cwd, ".git", "config");
2248
+ const gitConfigPath = join2(cwd, ".git", "config");
2226
2249
  if (await fileExists(gitConfigPath)) {
2227
2250
  try {
2228
2251
  const gitConfig = await readFile3(gitConfigPath, "utf-8");
@@ -2245,21 +2268,21 @@ async function detectProject(cwd) {
2245
2268
  } catch {
2246
2269
  }
2247
2270
  }
2248
- if (await fileExists(join3(cwd, ".github", "workflows"))) {
2271
+ if (await fileExists(join2(cwd, ".github", "workflows"))) {
2249
2272
  detected.cicd = "github_actions";
2250
- } else if (await fileExists(join3(cwd, ".gitlab-ci.yml"))) {
2273
+ } else if (await fileExists(join2(cwd, ".gitlab-ci.yml"))) {
2251
2274
  detected.cicd = "gitlab_ci";
2252
- } else if (await fileExists(join3(cwd, "Jenkinsfile"))) {
2275
+ } else if (await fileExists(join2(cwd, "Jenkinsfile"))) {
2253
2276
  detected.cicd = "jenkins";
2254
- } else if (await fileExists(join3(cwd, ".circleci"))) {
2277
+ } else if (await fileExists(join2(cwd, ".circleci"))) {
2255
2278
  detected.cicd = "circleci";
2256
- } else if (await fileExists(join3(cwd, ".travis.yml"))) {
2279
+ } else if (await fileExists(join2(cwd, ".travis.yml"))) {
2257
2280
  detected.cicd = "travis";
2258
- } else if (await fileExists(join3(cwd, "azure-pipelines.yml"))) {
2281
+ } else if (await fileExists(join2(cwd, "azure-pipelines.yml"))) {
2259
2282
  detected.cicd = "azure_devops";
2260
- } else if (await fileExists(join3(cwd, "bitbucket-pipelines.yml"))) {
2283
+ } else if (await fileExists(join2(cwd, "bitbucket-pipelines.yml"))) {
2261
2284
  detected.cicd = "bitbucket";
2262
- } else if (await fileExists(join3(cwd, ".drone.yml"))) {
2285
+ } else if (await fileExists(join2(cwd, ".drone.yml"))) {
2263
2286
  detected.cicd = "drone";
2264
2287
  }
2265
2288
  detected.existingFiles = [];
@@ -2277,12 +2300,12 @@ async function detectProject(cwd) {
2277
2300
  "CHANGELOG.md"
2278
2301
  ];
2279
2302
  for (const file of staticFiles) {
2280
- if (await fileExists(join3(cwd, file))) {
2303
+ if (await fileExists(join2(cwd, file))) {
2281
2304
  detected.existingFiles.push(file);
2282
2305
  }
2283
2306
  }
2284
2307
  if (!detected.description) {
2285
- const readmePath = join3(cwd, "README.md");
2308
+ const readmePath = join2(cwd, "README.md");
2286
2309
  if (await fileExists(readmePath)) {
2287
2310
  try {
2288
2311
  const readme = await readFile3(readmePath, "utf-8");
@@ -2739,7 +2762,7 @@ async function detectFromShallowClone(repoUrl) {
2739
2762
  return null;
2740
2763
  }
2741
2764
  try {
2742
- tempDir = await mkdtemp(join3(tmpdir(), "lynxprompt-detect-"));
2765
+ tempDir = await mkdtemp(join2(tmpdir(), "lynxprompt-detect-"));
2743
2766
  try {
2744
2767
  const result = spawnSync("git", ["clone", "--depth", "1", "--quiet", repoUrl, tempDir], {
2745
2768
  stdio: "pipe",
@@ -2801,13 +2824,13 @@ async function detectCommandFiles(cwd) {
2801
2824
  const commands = [];
2802
2825
  const { readdir: readdir4, readFile: readFileAsync } = await import("fs/promises");
2803
2826
  for (const cmdDir of COMMAND_DIRECTORIES) {
2804
- const dirPath = join3(cwd, cmdDir.directory);
2827
+ const dirPath = join2(cwd, cmdDir.directory);
2805
2828
  try {
2806
2829
  await access2(dirPath);
2807
2830
  const entries = await readdir4(dirPath, { withFileTypes: true });
2808
2831
  for (const entry of entries) {
2809
2832
  if (entry.isFile() && entry.name.endsWith(".md")) {
2810
- const filePath = join3(dirPath, entry.name);
2833
+ const filePath = join2(dirPath, entry.name);
2811
2834
  try {
2812
2835
  const content = await readFileAsync(filePath, "utf-8");
2813
2836
  const name = entry.name.replace(/\.md$/, "");
@@ -5686,9 +5709,9 @@ var TEST_FRAMEWORKS2 = TEST_FRAMEWORKS;
5686
5709
 
5687
5710
  // src/commands/wizard.ts
5688
5711
  var DRAFTS_DIR = ".lynxprompt/drafts";
5689
- var CLI_VERSION = "2.1.1";
5712
+ var CLI_VERSION = "2.1.2";
5690
5713
  async function saveDraftLocally(name, config2, stepReached) {
5691
- const draftsPath = join4(process.cwd(), DRAFTS_DIR);
5714
+ const draftsPath = join3(process.cwd(), DRAFTS_DIR);
5692
5715
  await mkdir3(draftsPath, { recursive: true });
5693
5716
  const draft = {
5694
5717
  name,
@@ -5699,12 +5722,12 @@ async function saveDraftLocally(name, config2, stepReached) {
5699
5722
  version: CLI_VERSION
5700
5723
  };
5701
5724
  const filename = `${name.replace(/[^a-zA-Z0-9-_]/g, "_")}.json`;
5702
- await writeFile3(join4(draftsPath, filename), JSON.stringify(draft, null, 2), "utf-8");
5725
+ await writeFile3(join3(draftsPath, filename), JSON.stringify(draft, null, 2), "utf-8");
5703
5726
  }
5704
5727
  async function loadDraftLocally(name) {
5705
5728
  try {
5706
5729
  const filename = `${name.replace(/[^a-zA-Z0-9-_]/g, "_")}.json`;
5707
- const filepath = join4(process.cwd(), DRAFTS_DIR, filename);
5730
+ const filepath = join3(process.cwd(), DRAFTS_DIR, filename);
5708
5731
  const content = await readFile4(filepath, "utf-8");
5709
5732
  return JSON.parse(content);
5710
5733
  } catch {
@@ -5713,7 +5736,7 @@ async function loadDraftLocally(name) {
5713
5736
  }
5714
5737
  async function listLocalDrafts() {
5715
5738
  try {
5716
- const draftsPath = join4(process.cwd(), DRAFTS_DIR);
5739
+ const draftsPath = join3(process.cwd(), DRAFTS_DIR);
5717
5740
  await access3(draftsPath);
5718
5741
  const files = await import("fs/promises").then((fs2) => fs2.readdir(draftsPath));
5719
5742
  return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
@@ -5752,20 +5775,20 @@ async function readMultilineInput(prompt) {
5752
5775
  input: process.stdin,
5753
5776
  output: process.stdout
5754
5777
  });
5755
- return new Promise((resolve) => {
5778
+ return new Promise((resolve2) => {
5756
5779
  const lines = [];
5757
5780
  let emptyLineCount = 0;
5758
5781
  rl.on("line", (line) => {
5759
5782
  if (line.trim() === "EOF") {
5760
5783
  rl.close();
5761
- resolve(lines.join("\n"));
5784
+ resolve2(lines.join("\n"));
5762
5785
  return;
5763
5786
  }
5764
5787
  if (line === "") {
5765
5788
  emptyLineCount++;
5766
5789
  if (emptyLineCount >= 2 && lines.length === 0) {
5767
5790
  rl.close();
5768
- resolve("");
5791
+ resolve2("");
5769
5792
  return;
5770
5793
  }
5771
5794
  } else {
@@ -5774,7 +5797,7 @@ async function readMultilineInput(prompt) {
5774
5797
  lines.push(line);
5775
5798
  });
5776
5799
  rl.on("close", () => {
5777
- resolve(lines.join("\n"));
5800
+ resolve2(lines.join("\n"));
5778
5801
  });
5779
5802
  });
5780
5803
  }
@@ -6729,7 +6752,7 @@ async function runWizardWithDraftProtection(options) {
6729
6752
  const isLocalPath = inputPath.startsWith("/") || inputPath.startsWith("~") || inputPath.startsWith(".");
6730
6753
  if (isLocalPath) {
6731
6754
  const { homedir } = await import("os");
6732
- const resolvedPath = inputPath.startsWith("~") ? inputPath.replace("~", homedir()) : inputPath.startsWith(".") ? join4(process.cwd(), inputPath) : inputPath;
6755
+ const resolvedPath = inputPath.startsWith("~") ? inputPath.replace("~", homedir()) : inputPath.startsWith(".") ? join3(process.cwd(), inputPath) : inputPath;
6733
6756
  const localSpinner = ora6(`Analyzing local repository at ${resolvedPath}...`).start();
6734
6757
  const localDetected = await detectProject(resolvedPath);
6735
6758
  if (localDetected) {
@@ -6883,7 +6906,7 @@ async function runWizardWithDraftProtection(options) {
6883
6906
  console.log();
6884
6907
  const outputDir = options.output || process.cwd();
6885
6908
  for (const [filename, content] of Object.entries(files)) {
6886
- const outputPath = join4(outputDir, filename);
6909
+ const outputPath = join3(outputDir, filename);
6887
6910
  let exists = false;
6888
6911
  try {
6889
6912
  await access3(outputPath);
@@ -8764,7 +8787,7 @@ async function runInteractiveWizard(options, detected) {
8764
8787
  for (const opt of STATIC_FILE_OPTIONS) {
8765
8788
  const filePath = STATIC_FILE_PATHS2[opt.value];
8766
8789
  if (filePath) {
8767
- const content = await readExistingFile(join4(process.cwd(), filePath));
8790
+ const content = await readExistingFile(join3(process.cwd(), filePath));
8768
8791
  if (content) {
8769
8792
  existingFiles[opt.value] = content;
8770
8793
  }
@@ -9100,7 +9123,7 @@ function handleApiError3(error) {
9100
9123
  // src/commands/status.ts
9101
9124
  import chalk9 from "chalk";
9102
9125
  import { readFile as readFile5, readdir, access as access4 } from "fs/promises";
9103
- import { join as join5 } from "path";
9126
+ import { join as join4 } from "path";
9104
9127
  import { existsSync as existsSync2 } from "fs";
9105
9128
  var CONFIG_FILES = [
9106
9129
  { path: "AGENTS.md", name: "AGENTS.md", platform: "Claude Code, Cursor, AI Agents" },
@@ -9123,10 +9146,10 @@ async function statusCommand() {
9123
9146
  console.log(chalk9.cyan("\u{1F431} LynxPrompt Status"));
9124
9147
  console.log(chalk9.gray(` Directory: ${cwd}`));
9125
9148
  console.log();
9126
- const lynxpromptExists = existsSync2(join5(cwd, ".lynxprompt"));
9149
+ const lynxpromptExists = existsSync2(join4(cwd, ".lynxprompt"));
9127
9150
  if (lynxpromptExists) {
9128
9151
  console.log(chalk9.green("\u2713 LynxPrompt initialized"));
9129
- const configPath = join5(cwd, ".lynxprompt/conf.yml");
9152
+ const configPath = join4(cwd, ".lynxprompt/conf.yml");
9130
9153
  if (existsSync2(configPath)) {
9131
9154
  try {
9132
9155
  const content = await readFile5(configPath, "utf-8");
@@ -9189,7 +9212,7 @@ async function statusCommand() {
9189
9212
  console.log();
9190
9213
  let foundAny = false;
9191
9214
  for (const config2 of CONFIG_FILES) {
9192
- const filePath = join5(cwd, config2.path);
9215
+ const filePath = join4(cwd, config2.path);
9193
9216
  try {
9194
9217
  await access4(filePath);
9195
9218
  const content = await readFile5(filePath, "utf-8");
@@ -9210,7 +9233,7 @@ async function statusCommand() {
9210
9233
  }
9211
9234
  }
9212
9235
  for (const config2 of CONFIG_DIRS) {
9213
- const dirPath = join5(cwd, config2.path);
9236
+ const dirPath = join4(cwd, config2.path);
9214
9237
  if (existsSync2(dirPath)) {
9215
9238
  try {
9216
9239
  const files = await readdir(dirPath);
@@ -9271,7 +9294,7 @@ function formatBytes(bytes) {
9271
9294
  import chalk10 from "chalk";
9272
9295
  import ora8 from "ora";
9273
9296
  import { readFile as readFile6, readdir as readdir2, stat } from "fs/promises";
9274
- import { join as join6 } from "path";
9297
+ import { join as join5 } from "path";
9275
9298
  import { existsSync as existsSync3 } from "fs";
9276
9299
  import * as yaml2 from "yaml";
9277
9300
  var CONFIG_FILES2 = [
@@ -9329,7 +9352,7 @@ function validateMarkdown(content, filename) {
9329
9352
  async function validateLynxPromptConfig(cwd) {
9330
9353
  const errors = [];
9331
9354
  const warnings = [];
9332
- const configPath = join6(cwd, ".lynxprompt/conf.yml");
9355
+ const configPath = join5(cwd, ".lynxprompt/conf.yml");
9333
9356
  if (!existsSync3(configPath)) {
9334
9357
  return { errors, warnings };
9335
9358
  }
@@ -9349,7 +9372,7 @@ async function validateLynxPromptConfig(cwd) {
9349
9372
  } else {
9350
9373
  for (const source of config2.sources) {
9351
9374
  if (source.type === "local" && source.path) {
9352
- const sourcePath = join6(cwd, source.path);
9375
+ const sourcePath = join5(cwd, source.path);
9353
9376
  if (!existsSync3(sourcePath)) {
9354
9377
  errors.push(`.lynxprompt/conf.yml: Source path not found: ${source.path}`);
9355
9378
  }
@@ -9403,7 +9426,7 @@ async function checkCommand(options = {}) {
9403
9426
  };
9404
9427
  const spinner = !isCi ? ora8("Scanning for configuration files...").start() : null;
9405
9428
  for (const file of CONFIG_FILES2) {
9406
- const filePath = join6(cwd, file.path);
9429
+ const filePath = join5(cwd, file.path);
9407
9430
  if (existsSync3(filePath)) {
9408
9431
  result.files.push(file.path);
9409
9432
  try {
@@ -9417,12 +9440,12 @@ async function checkCommand(options = {}) {
9417
9440
  }
9418
9441
  }
9419
9442
  for (const dir of CONFIG_DIRS2) {
9420
- const dirPath = join6(cwd, dir.path);
9443
+ const dirPath = join5(cwd, dir.path);
9421
9444
  if (existsSync3(dirPath)) {
9422
9445
  try {
9423
9446
  const files = await readdir2(dirPath);
9424
9447
  for (const file of files) {
9425
- const filePath = join6(dirPath, file);
9448
+ const filePath = join5(dirPath, file);
9426
9449
  const fileStat = await stat(filePath);
9427
9450
  if (fileStat.isFile()) {
9428
9451
  result.files.push(`${dir.path}/${file}`);
@@ -9503,7 +9526,7 @@ async function checkCommand(options = {}) {
9503
9526
  import chalk11 from "chalk";
9504
9527
  import ora9 from "ora";
9505
9528
  import { readFile as readFile7 } from "fs/promises";
9506
- import { join as join7 } from "path";
9529
+ import { join as join6 } from "path";
9507
9530
  import { existsSync as existsSync4 } from "fs";
9508
9531
  function computeDiff(oldText, newText) {
9509
9532
  const oldLines = oldText.split("\n");
@@ -9651,7 +9674,7 @@ async function diffCommand(fileOrId, options = {}) {
9651
9674
  console.log();
9652
9675
  }
9653
9676
  async function diffFileWithBlueprint(cwd, file, blueprintId, compact = false) {
9654
- const filePath = join7(cwd, file);
9677
+ const filePath = join6(cwd, file);
9655
9678
  if (!existsSync4(filePath)) {
9656
9679
  console.log(chalk11.red(`\u2717 File not found: ${file}`));
9657
9680
  return;
@@ -9717,7 +9740,7 @@ async function diffWithBlueprintId(cwd, blueprintId) {
9717
9740
  let localContent = null;
9718
9741
  let localPath = null;
9719
9742
  for (const path2 of localPaths) {
9720
- const fullPath = join7(cwd, path2);
9743
+ const fullPath = join6(cwd, path2);
9721
9744
  if (existsSync4(fullPath)) {
9722
9745
  try {
9723
9746
  localContent = await readFile7(fullPath, "utf-8");
@@ -9767,7 +9790,7 @@ async function diffWithBlueprintId(cwd, blueprintId) {
9767
9790
  }
9768
9791
  }
9769
9792
  async function diffLocal(cwd) {
9770
- const rulesDir = join7(cwd, ".lynxprompt/rules");
9793
+ const rulesDir = join6(cwd, ".lynxprompt/rules");
9771
9794
  if (!existsSync4(rulesDir)) {
9772
9795
  console.log(chalk11.yellow("\u26A0 No .lynxprompt/rules/ directory found."));
9773
9796
  console.log(chalk11.gray("Run 'lynxp init' to set up the advanced workflow, or 'lynxp wizard' for simple file generation."));
@@ -9775,7 +9798,7 @@ async function diffLocal(cwd) {
9775
9798
  }
9776
9799
  console.log(chalk11.gray("Comparing .lynxprompt/rules/ with exported files..."));
9777
9800
  console.log();
9778
- const rulesPath = join7(rulesDir, "agents.md");
9801
+ const rulesPath = join6(rulesDir, "agents.md");
9779
9802
  if (!existsSync4(rulesPath)) {
9780
9803
  console.log(chalk11.yellow("\u26A0 No rules files found in .lynxprompt/rules/"));
9781
9804
  return;
@@ -9793,7 +9816,7 @@ async function diffLocal(cwd) {
9793
9816
  ];
9794
9817
  let hasChanges = false;
9795
9818
  for (const file of exportedFiles) {
9796
- const filePath = join7(cwd, file.path);
9819
+ const filePath = join6(cwd, file.path);
9797
9820
  if (existsSync4(filePath)) {
9798
9821
  try {
9799
9822
  const exportedContent = await readFile7(filePath, "utf-8");
@@ -9834,7 +9857,7 @@ async function diffLocal(cwd) {
9834
9857
  import chalk12 from "chalk";
9835
9858
  import ora10 from "ora";
9836
9859
  import prompts4 from "prompts";
9837
- import { join as join8 } from "path";
9860
+ import { join as join7 } from "path";
9838
9861
  import { existsSync as existsSync5 } from "fs";
9839
9862
  function getSourceFromVisibility2(visibility) {
9840
9863
  switch (visibility) {
@@ -9869,7 +9892,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
9869
9892
  ".zed/instructions.md",
9870
9893
  ".clinerules"
9871
9894
  ];
9872
- const foundFiles = configFiles.filter((f) => existsSync5(join8(cwd, f)));
9895
+ const foundFiles = configFiles.filter((f) => existsSync5(join7(cwd, f)));
9873
9896
  if (foundFiles.length === 0) {
9874
9897
  console.log(chalk12.yellow("No AI configuration files found in this directory."));
9875
9898
  console.log();
@@ -9896,7 +9919,7 @@ async function linkCommand(fileArg, blueprintIdArg, options = {}) {
9896
9919
  } else {
9897
9920
  file = fileArg;
9898
9921
  }
9899
- const filePath = join8(cwd, file);
9922
+ const filePath = join7(cwd, file);
9900
9923
  if (!existsSync5(filePath)) {
9901
9924
  console.log(chalk12.red(`\u2717 File not found: ${file}`));
9902
9925
  return;
@@ -10326,7 +10349,7 @@ async function analyzeCommand(options) {
10326
10349
  // src/commands/convert.ts
10327
10350
  import chalk14 from "chalk";
10328
10351
  import { readFile as readFile8, writeFile as writeFile4, access as access5 } from "fs/promises";
10329
- import { join as join9, basename } from "path";
10352
+ import { join as join8, basename } from "path";
10330
10353
  import ora12 from "ora";
10331
10354
  var SOURCE_FILES = {
10332
10355
  "agents.md": "agents",
@@ -10441,7 +10464,7 @@ var isCommandTarget = (target) => target.includes("-command") || target.includes
10441
10464
  async function detectSourceFile(cwd) {
10442
10465
  for (const [pattern, platform2] of Object.entries(SOURCE_FILES)) {
10443
10466
  try {
10444
- const fullPath = join9(cwd, pattern);
10467
+ const fullPath = join8(cwd, pattern);
10445
10468
  await access5(fullPath);
10446
10469
  return { path: fullPath, platform: platform2 };
10447
10470
  } catch {
@@ -10450,7 +10473,7 @@ async function detectSourceFile(cwd) {
10450
10473
  const uppercaseVariants = ["AGENTS.md", "CLAUDE.md"];
10451
10474
  for (const variant of uppercaseVariants) {
10452
10475
  try {
10453
- const fullPath = join9(cwd, variant);
10476
+ const fullPath = join8(cwd, variant);
10454
10477
  await access5(fullPath);
10455
10478
  const platform2 = variant.toLowerCase().replace(".md", "");
10456
10479
  return { path: fullPath, platform: platform2 };
@@ -10571,7 +10594,7 @@ async function convertCommand(source, target, options) {
10571
10594
  let sourcePath;
10572
10595
  let sourcePlatform;
10573
10596
  if (source) {
10574
- sourcePath = join9(cwd, source);
10597
+ sourcePath = join8(cwd, source);
10575
10598
  const cmdPlatform = detectCommandPlatform(source);
10576
10599
  if (cmdPlatform) {
10577
10600
  sourcePlatform = cmdPlatform;
@@ -10633,7 +10656,7 @@ async function convertCommand(source, target, options) {
10633
10656
  const targetDir = TARGET_FILES[normalizedTarget].split("/").slice(0, -1).join("/");
10634
10657
  outputFilename = `${targetDir}/${originalFilename}`;
10635
10658
  }
10636
- const outputPath = join9(cwd, outputFilename);
10659
+ const outputPath = join8(cwd, outputFilename);
10637
10660
  try {
10638
10661
  await access5(outputPath);
10639
10662
  if (!options.force) {
@@ -10645,7 +10668,7 @@ async function convertCommand(source, target, options) {
10645
10668
  }
10646
10669
  try {
10647
10670
  const { mkdir: mkdir4 } = await import("fs/promises");
10648
- const outputDir = join9(cwd, outputFilename.split("/").slice(0, -1).join("/"));
10671
+ const outputDir = join8(cwd, outputFilename.split("/").slice(0, -1).join("/"));
10649
10672
  if (outputDir !== cwd) {
10650
10673
  await mkdir4(outputDir, { recursive: true });
10651
10674
  }
@@ -10664,7 +10687,7 @@ async function convertCommand(source, target, options) {
10664
10687
  // src/commands/merge.ts
10665
10688
  import chalk15 from "chalk";
10666
10689
  import { readFile as readFile9, writeFile as writeFile5, access as access6 } from "fs/promises";
10667
- import { join as join10, basename as basename2 } from "path";
10690
+ import { join as join9, basename as basename2 } from "path";
10668
10691
  import ora13 from "ora";
10669
10692
  import prompts5 from "prompts";
10670
10693
  function parseMarkdownSections(content, sourceName) {
@@ -10789,7 +10812,7 @@ async function mergeCommand(files, options) {
10789
10812
  console.log();
10790
10813
  const allSections = [];
10791
10814
  for (const file of files) {
10792
- const filePath = join10(cwd, file);
10815
+ const filePath = join9(cwd, file);
10793
10816
  try {
10794
10817
  await access6(filePath);
10795
10818
  } catch {
@@ -10846,7 +10869,7 @@ async function mergeCommand(files, options) {
10846
10869
  ${mergedContent}
10847
10870
  `;
10848
10871
  const outputFilename = options.output || "merged.md";
10849
- const outputPath = join10(cwd, outputFilename);
10872
+ const outputPath = join9(cwd, outputFilename);
10850
10873
  try {
10851
10874
  await access6(outputPath);
10852
10875
  if (!options.force) {
@@ -10875,7 +10898,7 @@ import chalk16 from "chalk";
10875
10898
  import prompts6 from "prompts";
10876
10899
  import ora14 from "ora";
10877
10900
  import { readFile as readFile10, access as access7, readdir as readdir3 } from "fs/promises";
10878
- import { join as join11, relative, dirname as dirname4, basename as basename3 } from "path";
10901
+ import { join as join10, relative as relative2, dirname as dirname4, basename as basename3 } from "path";
10879
10902
  var CONFIG_FILE_PATTERNS = [
10880
10903
  "AGENTS.md",
10881
10904
  "CLAUDE.md",
@@ -10892,7 +10915,7 @@ async function scanDirectory(rootPath, options = {}) {
10892
10915
  try {
10893
10916
  const entries = await readdir3(dir, { withFileTypes: true });
10894
10917
  for (const entry of entries) {
10895
- const fullPath = join11(dir, entry.name);
10918
+ const fullPath = join10(dir, entry.name);
10896
10919
  if (entry.isDirectory()) {
10897
10920
  const skipDirs = [
10898
10921
  "node_modules",
@@ -10919,7 +10942,7 @@ async function scanDirectory(rootPath, options = {}) {
10919
10942
  if (patterns.includes(entry.name)) {
10920
10943
  try {
10921
10944
  const content = await readFile10(fullPath, "utf-8");
10922
- const relativePath = relative(rootPath, fullPath);
10945
+ const relativePath = relative2(rootPath, fullPath);
10923
10946
  files.push({
10924
10947
  path: fullPath,
10925
10948
  relativePath,
@@ -11080,7 +11103,7 @@ async function importCommand(path2 = ".", options) {
11080
11103
  console.log(chalk16.cyan.bold(" \u{1F4E5} LynxPrompt Import"));
11081
11104
  console.log(chalk16.gray(" Scan and import AGENTS.md files and AI commands from your repository"));
11082
11105
  console.log();
11083
- const rootPath = join11(process.cwd(), path2);
11106
+ const rootPath = join10(process.cwd(), path2);
11084
11107
  try {
11085
11108
  await access7(rootPath);
11086
11109
  } catch {
@@ -11120,7 +11143,7 @@ async function importCommand(path2 = ".", options) {
11120
11143
  console.log(chalk16.blue(` Cursor Commands (${cursorCommands.length}):`));
11121
11144
  for (const cmd of cursorCommands) {
11122
11145
  console.log(chalk16.gray(` \u26A1 ${cmd.name}`));
11123
- console.log(chalk16.gray(` ${relative(rootPath, cmd.path)}`));
11146
+ console.log(chalk16.gray(` ${relative2(rootPath, cmd.path)}`));
11124
11147
  }
11125
11148
  console.log();
11126
11149
  }
@@ -11128,7 +11151,7 @@ async function importCommand(path2 = ".", options) {
11128
11151
  console.log(chalk16.yellow(` Claude Commands (${claudeCommands.length}):`));
11129
11152
  for (const cmd of claudeCommands) {
11130
11153
  console.log(chalk16.gray(` \u{1F9E0} ${cmd.name}`));
11131
- console.log(chalk16.gray(` ${relative(rootPath, cmd.path)}`));
11154
+ console.log(chalk16.gray(` ${relative2(rootPath, cmd.path)}`));
11132
11155
  }
11133
11156
  console.log();
11134
11157
  }
@@ -11177,7 +11200,7 @@ async function importCommand(path2 = ".", options) {
11177
11200
  }
11178
11201
  async function saveHierarchyInfo(rootPath, result) {
11179
11202
  const { writeFile: writeFile6, mkdir: mkdir4 } = await import("fs/promises");
11180
- const configDir = join11(rootPath, ".lynxprompt");
11203
+ const configDir = join10(rootPath, ".lynxprompt");
11181
11204
  try {
11182
11205
  await mkdir4(configDir, { recursive: true });
11183
11206
  const hierarchyInfo = {
@@ -11196,7 +11219,7 @@ async function saveHierarchyInfo(rootPath, result) {
11196
11219
  }))
11197
11220
  };
11198
11221
  await writeFile6(
11199
- join11(configDir, "hierarchy.json"),
11222
+ join10(configDir, "hierarchy.json"),
11200
11223
  JSON.stringify(hierarchyInfo, null, 2),
11201
11224
  "utf-8"
11202
11225
  );
@@ -11338,7 +11361,7 @@ async function configCommand(action, valueArg) {
11338
11361
  }
11339
11362
 
11340
11363
  // src/index.ts
11341
- var CLI_VERSION2 = "2.1.1";
11364
+ var CLI_VERSION2 = "2.1.2";
11342
11365
  var program = new Command();
11343
11366
  program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version(CLI_VERSION2);
11344
11367
  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);