lynxprompt 1.2.11 → 1.2.13

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
@@ -1549,6 +1549,7 @@ async function detectProject(cwd) {
1549
1549
  const detected = {
1550
1550
  name: null,
1551
1551
  stack: [],
1552
+ databases: [],
1552
1553
  commands: {},
1553
1554
  packageManager: null,
1554
1555
  type: "unknown"
@@ -1879,6 +1880,8 @@ function parseGitLabUrl(url) {
1879
1880
  }
1880
1881
  return null;
1881
1882
  }
1883
+ var OPEN_SOURCE_LICENSES = ["mit", "apache-2.0", "gpl-3.0", "lgpl-3.0", "agpl-3.0", "bsd-2-clause", "bsd-3-clause", "mpl-2.0", "unlicense", "cc0-1.0", "isc"];
1884
+ var STATIC_FILES = [".editorconfig", "CONTRIBUTING.md", "CODE_OF_CONDUCT.md", "SECURITY.md", "ROADMAP.md", ".gitignore", "LICENSE", "README.md", "ARCHITECTURE.md", "CHANGELOG.md"];
1882
1885
  async function detectFromGitHubApi(repoUrl) {
1883
1886
  const parsed = parseGitHubUrl(repoUrl);
1884
1887
  if (!parsed) return null;
@@ -1890,17 +1893,24 @@ async function detectFromGitHubApi(repoUrl) {
1890
1893
  if (!repoRes.ok) return null;
1891
1894
  const repoInfo = await repoRes.json();
1892
1895
  if (repoInfo.private) return null;
1896
+ const licenseId = repoInfo.license?.spdx_id?.toLowerCase() || null;
1897
+ const isOpenSource = !repoInfo.private && (licenseId ? OPEN_SOURCE_LICENSES.includes(licenseId) : false);
1893
1898
  const detected = {
1894
1899
  name: repoInfo.name,
1895
1900
  description: repoInfo.description ?? void 0,
1896
1901
  stack: [],
1902
+ databases: [],
1897
1903
  commands: {},
1898
1904
  packageManager: null,
1899
1905
  type: "application",
1900
1906
  repoHost: "github",
1901
1907
  repoUrl,
1902
- license: repoInfo.license?.spdx_id?.toLowerCase(),
1903
- isPublicRepo: !repoInfo.private
1908
+ license: licenseId ?? void 0,
1909
+ isPublicRepo: !repoInfo.private,
1910
+ isOpenSource,
1911
+ projectType: isOpenSource ? "open_source" : void 0,
1912
+ hasDocker: false,
1913
+ existingFiles: []
1904
1914
  };
1905
1915
  const filesRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/`, {
1906
1916
  headers: { "User-Agent": "LynxPrompt-CLI" }
@@ -1908,32 +1918,108 @@ async function detectFromGitHubApi(repoUrl) {
1908
1918
  if (!filesRes.ok) return detected;
1909
1919
  const files = await filesRes.json();
1910
1920
  const fileNames = new Set(files.map((f) => f.name.toLowerCase()));
1921
+ for (const file of STATIC_FILES) {
1922
+ if (files.some((f) => f.name.toLowerCase() === file.toLowerCase())) {
1923
+ detected.existingFiles.push(file);
1924
+ }
1925
+ }
1926
+ if (fileNames.has("dockerfile") || fileNames.has("docker-compose.yml") || fileNames.has("docker-compose.yaml")) {
1927
+ detected.hasDocker = true;
1928
+ detected.stack.push("docker");
1929
+ const dockerComposeFile = fileNames.has("docker-compose.yml") ? "docker-compose.yml" : fileNames.has("docker-compose.yaml") ? "docker-compose.yaml" : null;
1930
+ if (dockerComposeFile) {
1931
+ const composeRes = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/HEAD/${dockerComposeFile}`);
1932
+ if (composeRes.ok) {
1933
+ try {
1934
+ const content = await composeRes.text();
1935
+ const lowerContent = content.toLowerCase();
1936
+ if (content.includes("ghcr.io")) detected.containerRegistry = "ghcr";
1937
+ else if (content.includes("docker.io") || /image:\s*[a-z0-9]+\/[a-z0-9]/.test(content)) detected.containerRegistry = "dockerhub";
1938
+ else if (content.includes("gcr.io")) detected.containerRegistry = "gcr";
1939
+ else if (content.includes("ecr.") || content.includes(".amazonaws.com")) detected.containerRegistry = "ecr";
1940
+ else if (content.includes("azurecr.io")) detected.containerRegistry = "acr";
1941
+ else if (content.includes("quay.io")) detected.containerRegistry = "quay";
1942
+ else if (content.includes("registry.gitlab.com")) detected.containerRegistry = "gitlab_registry";
1943
+ if (lowerContent.includes("postgres")) detected.databases.push("postgresql");
1944
+ if (lowerContent.includes("mysql") && !lowerContent.includes("mysql-")) detected.databases.push("mysql");
1945
+ if (lowerContent.includes("mongo")) detected.databases.push("mongodb");
1946
+ if (lowerContent.includes("redis")) detected.databases.push("redis");
1947
+ if (lowerContent.includes("sqlite")) detected.databases.push("sqlite");
1948
+ if (lowerContent.includes("mariadb")) detected.databases.push("mariadb");
1949
+ } catch {
1950
+ }
1951
+ }
1952
+ }
1953
+ }
1954
+ if (files.some((f) => f.name === ".github" && f.type === "dir")) {
1955
+ const ghFilesRes = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/.github`, {
1956
+ headers: { "User-Agent": "LynxPrompt-CLI" }
1957
+ });
1958
+ if (ghFilesRes.ok) {
1959
+ const ghFiles = await ghFilesRes.json();
1960
+ if (ghFiles.some((f) => f.name === "workflows")) {
1961
+ detected.cicd = "github_actions";
1962
+ }
1963
+ }
1964
+ }
1965
+ if (fileNames.has(".gitlab-ci.yml")) detected.cicd = "gitlab_ci";
1966
+ if (fileNames.has("jenkinsfile")) detected.cicd = "jenkins";
1967
+ if (fileNames.has(".travis.yml")) detected.cicd = "travis";
1968
+ if (fileNames.has("azure-pipelines.yml")) detected.cicd = "azure_devops";
1911
1969
  if (fileNames.has("pyproject.toml")) {
1912
1970
  detected.stack.push("python");
1913
1971
  const pyprojectRes = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/HEAD/pyproject.toml`);
1914
1972
  if (pyprojectRes.ok) {
1915
1973
  try {
1916
1974
  const content = await pyprojectRes.text();
1917
- if (content.includes("fastapi")) detected.stack.push("fastapi");
1918
- if (content.includes("django")) detected.stack.push("django");
1919
- if (content.includes("flask")) detected.stack.push("flask");
1920
- if (content.includes("sqlalchemy")) detected.stack.push("sqlalchemy");
1921
- if (content.includes("pydantic")) detected.stack.push("pydantic");
1922
- if (content.includes("pytest")) detected.stack.push("pytest");
1923
- if (content.includes("ruff")) detected.stack.push("ruff");
1975
+ const lowerContent = content.toLowerCase();
1976
+ if (lowerContent.includes("fastapi")) detected.stack.push("fastapi");
1977
+ if (lowerContent.includes("django")) detected.stack.push("django");
1978
+ if (lowerContent.includes("flask")) detected.stack.push("flask");
1979
+ if (lowerContent.includes("sqlalchemy")) detected.stack.push("sqlalchemy");
1980
+ if (lowerContent.includes("pydantic")) detected.stack.push("pydantic");
1981
+ if (lowerContent.includes("pytest")) detected.testFramework = "pytest";
1982
+ else if (lowerContent.includes("unittest")) detected.testFramework = "unittest";
1983
+ detected.commands.test = "pytest";
1984
+ if (lowerContent.includes("ruff")) detected.commands.lint = "ruff check .";
1985
+ if (lowerContent.includes("asyncpg") || lowerContent.includes("psycopg")) {
1986
+ if (!detected.databases.includes("postgresql")) detected.databases.push("postgresql");
1987
+ }
1988
+ if (lowerContent.includes("aiosqlite") || lowerContent.includes("sqlite")) {
1989
+ if (!detected.databases.includes("sqlite")) detected.databases.push("sqlite");
1990
+ }
1991
+ if (lowerContent.includes("pymongo") || lowerContent.includes("motor")) {
1992
+ if (!detected.databases.includes("mongodb")) detected.databases.push("mongodb");
1993
+ }
1994
+ if (lowerContent.includes("redis") || lowerContent.includes("aioredis")) {
1995
+ if (!detected.databases.includes("redis")) detected.databases.push("redis");
1996
+ }
1997
+ if (lowerContent.includes("pymysql") || lowerContent.includes("aiomysql")) {
1998
+ if (!detected.databases.includes("mysql")) detected.databases.push("mysql");
1999
+ }
1924
2000
  } catch {
1925
2001
  }
1926
2002
  }
1927
- } else if (fileNames.has("requirements.txt")) {
1928
- detected.stack.push("python");
2003
+ }
2004
+ if (fileNames.has("requirements.txt")) {
1929
2005
  const reqRes = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/HEAD/requirements.txt`);
1930
2006
  if (reqRes.ok) {
1931
2007
  try {
1932
2008
  const content = (await reqRes.text()).toLowerCase();
1933
- if (content.includes("fastapi")) detected.stack.push("fastapi");
1934
- if (content.includes("django")) detected.stack.push("django");
1935
- if (content.includes("flask")) detected.stack.push("flask");
1936
- if (content.includes("sqlalchemy")) detected.stack.push("sqlalchemy");
2009
+ if (!detected.stack.includes("python")) detected.stack.push("python");
2010
+ if (content.includes("fastapi") && !detected.stack.includes("fastapi")) detected.stack.push("fastapi");
2011
+ if (content.includes("django") && !detected.stack.includes("django")) detected.stack.push("django");
2012
+ if (content.includes("flask") && !detected.stack.includes("flask")) detected.stack.push("flask");
2013
+ if (content.includes("sqlalchemy") && !detected.stack.includes("sqlalchemy")) detected.stack.push("sqlalchemy");
2014
+ if (content.includes("asyncpg") || content.includes("psycopg")) {
2015
+ if (!detected.databases.includes("postgresql")) detected.databases.push("postgresql");
2016
+ }
2017
+ if (content.includes("aiosqlite") || content.includes("sqlite")) {
2018
+ if (!detected.databases.includes("sqlite")) detected.databases.push("sqlite");
2019
+ }
2020
+ if (content.includes("pymongo") || content.includes("motor")) {
2021
+ if (!detected.databases.includes("mongodb")) detected.databases.push("mongodb");
2022
+ }
1937
2023
  } catch {
1938
2024
  }
1939
2025
  }
@@ -1950,11 +2036,16 @@ async function detectFromGitHubApi(repoUrl) {
1950
2036
  if (allDeps["svelte"]) detected.stack.push("svelte");
1951
2037
  if (allDeps["express"]) detected.stack.push("express");
1952
2038
  if (allDeps["fastify"]) detected.stack.push("fastify");
2039
+ if (allDeps["hono"]) detected.stack.push("hono");
1953
2040
  if (allDeps["typescript"]) detected.stack.push("typescript");
1954
2041
  if (allDeps["tailwindcss"]) detected.stack.push("tailwind");
1955
2042
  if (allDeps["prisma"]) detected.stack.push("prisma");
1956
- if (allDeps["vitest"]) detected.stack.push("vitest");
1957
- if (allDeps["jest"]) detected.stack.push("jest");
2043
+ if (allDeps["drizzle-orm"]) detected.stack.push("drizzle");
2044
+ if (allDeps["vitest"]) detected.testFramework = "vitest";
2045
+ else if (allDeps["jest"]) detected.testFramework = "jest";
2046
+ else if (allDeps["@playwright/test"]) detected.testFramework = "playwright";
2047
+ else if (allDeps["cypress"]) detected.testFramework = "cypress";
2048
+ else if (allDeps["mocha"]) detected.testFramework = "mocha";
1958
2049
  if (detected.stack.length === 0 || detected.stack.length === 1 && detected.stack[0] === "typescript") {
1959
2050
  detected.stack.unshift("javascript");
1960
2051
  }
@@ -1965,30 +2056,35 @@ async function detectFromGitHubApi(repoUrl) {
1965
2056
  if (pkg.scripts.dev) detected.commands.dev = "npm run dev";
1966
2057
  else if (pkg.scripts.start) detected.commands.dev = "npm run start";
1967
2058
  }
2059
+ if (allDeps["pg"] || allDeps["postgres"] || allDeps["@neondatabase/serverless"]) {
2060
+ if (!detected.databases.includes("postgresql")) detected.databases.push("postgresql");
2061
+ }
2062
+ if (allDeps["better-sqlite3"] || allDeps["sql.js"] || allDeps["sqlite3"]) {
2063
+ if (!detected.databases.includes("sqlite")) detected.databases.push("sqlite");
2064
+ }
2065
+ if (allDeps["mongodb"] || allDeps["mongoose"]) {
2066
+ if (!detected.databases.includes("mongodb")) detected.databases.push("mongodb");
2067
+ }
2068
+ if (allDeps["redis"] || allDeps["ioredis"]) {
2069
+ if (!detected.databases.includes("redis")) detected.databases.push("redis");
2070
+ }
2071
+ if (allDeps["mysql"] || allDeps["mysql2"]) {
2072
+ if (!detected.databases.includes("mysql")) detected.databases.push("mysql");
2073
+ }
1968
2074
  } catch {
1969
2075
  }
1970
2076
  }
1971
2077
  }
1972
- if (fileNames.has("cargo.toml")) detected.stack.push("rust");
1973
- if (fileNames.has("go.mod")) detected.stack.push("go");
1974
- if (fileNames.has("dockerfile")) detected.hasDocker = true;
1975
- if (fileNames.has("docker-compose.yml") || fileNames.has("docker-compose.yaml")) {
1976
- const composeRes = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/HEAD/docker-compose.yml`);
1977
- if (composeRes.ok) {
1978
- try {
1979
- const content = (await composeRes.text()).toLowerCase();
1980
- if (content.includes("postgres")) detected.stack.push("postgresql");
1981
- if (content.includes("mysql")) detected.stack.push("mysql");
1982
- if (content.includes("mongo")) detected.stack.push("mongodb");
1983
- if (content.includes("redis")) detected.stack.push("redis");
1984
- } catch {
1985
- }
1986
- }
2078
+ if (fileNames.has("cargo.toml")) {
2079
+ detected.stack.push("rust");
2080
+ detected.commands.build = "cargo build";
2081
+ detected.commands.test = "cargo test";
1987
2082
  }
1988
- if (files.some((f) => f.name === ".github" && f.type === "dir")) {
1989
- detected.cicd = "github_actions";
2083
+ if (fileNames.has("go.mod")) {
2084
+ detected.stack.push("go");
2085
+ detected.commands.build = "go build";
2086
+ detected.commands.test = "go test ./...";
1990
2087
  }
1991
- if (fileNames.has(".gitlab-ci.yml")) detected.cicd = "gitlab_ci";
1992
2088
  return detected;
1993
2089
  } catch {
1994
2090
  return null;
@@ -2006,16 +2102,24 @@ async function detectFromGitLabApi(repoUrl) {
2006
2102
  if (!repoRes.ok) return null;
2007
2103
  const repoInfo = await repoRes.json();
2008
2104
  if (repoInfo.visibility === "private") return null;
2105
+ const licenseId = repoInfo.license?.key?.toLowerCase() || null;
2106
+ const isOpenSource = repoInfo.visibility === "public" && (licenseId ? OPEN_SOURCE_LICENSES.includes(licenseId) : false);
2009
2107
  const detected = {
2010
2108
  name: repoInfo.name,
2011
2109
  description: repoInfo.description ?? void 0,
2012
2110
  stack: [],
2111
+ databases: [],
2013
2112
  commands: {},
2014
2113
  packageManager: null,
2015
2114
  type: "application",
2016
2115
  repoHost: "gitlab",
2017
2116
  repoUrl,
2018
- license: repoInfo.license?.key?.toLowerCase()
2117
+ license: licenseId ?? void 0,
2118
+ isPublicRepo: repoInfo.visibility === "public",
2119
+ isOpenSource,
2120
+ projectType: isOpenSource ? "open_source" : void 0,
2121
+ hasDocker: false,
2122
+ existingFiles: []
2019
2123
  };
2020
2124
  const filesRes = await fetch(`https://${host}/api/v4/projects/${encodedPath}/repository/tree?per_page=100`, {
2021
2125
  headers: { "User-Agent": "LynxPrompt-CLI" }
@@ -2023,6 +2127,39 @@ async function detectFromGitLabApi(repoUrl) {
2023
2127
  if (!filesRes.ok) return detected;
2024
2128
  const files = await filesRes.json();
2025
2129
  const fileNames = new Set(files.map((f) => f.name.toLowerCase()));
2130
+ for (const file of STATIC_FILES) {
2131
+ if (files.some((f) => f.name.toLowerCase() === file.toLowerCase())) {
2132
+ detected.existingFiles.push(file);
2133
+ }
2134
+ }
2135
+ if (fileNames.has(".gitlab-ci.yml")) detected.cicd = "gitlab_ci";
2136
+ if (fileNames.has("jenkinsfile")) detected.cicd = "jenkins";
2137
+ if (fileNames.has("dockerfile") || fileNames.has("docker-compose.yml") || fileNames.has("docker-compose.yaml")) {
2138
+ detected.hasDocker = true;
2139
+ detected.stack.push("docker");
2140
+ const dockerComposeFile = fileNames.has("docker-compose.yml") ? "docker-compose.yml" : fileNames.has("docker-compose.yaml") ? "docker-compose.yaml" : null;
2141
+ if (dockerComposeFile) {
2142
+ const composeRes = await fetch(`https://${host}/api/v4/projects/${encodedPath}/repository/files/${encodeURIComponent(dockerComposeFile)}/raw?ref=HEAD`, {
2143
+ headers: { "User-Agent": "LynxPrompt-CLI" }
2144
+ });
2145
+ if (composeRes.ok) {
2146
+ try {
2147
+ const content = await composeRes.text();
2148
+ const lowerContent = content.toLowerCase();
2149
+ if (content.includes("registry.gitlab.com")) detected.containerRegistry = "gitlab_registry";
2150
+ else if (content.includes("ghcr.io")) detected.containerRegistry = "ghcr";
2151
+ else if (content.includes("docker.io") || /image:\s*[a-z0-9]+\/[a-z0-9]/.test(content)) detected.containerRegistry = "dockerhub";
2152
+ else if (content.includes("gcr.io")) detected.containerRegistry = "gcr";
2153
+ if (lowerContent.includes("postgres")) detected.databases.push("postgresql");
2154
+ if (lowerContent.includes("mysql") && !lowerContent.includes("mysql-")) detected.databases.push("mysql");
2155
+ if (lowerContent.includes("mongo")) detected.databases.push("mongodb");
2156
+ if (lowerContent.includes("redis")) detected.databases.push("redis");
2157
+ if (lowerContent.includes("sqlite")) detected.databases.push("sqlite");
2158
+ } catch {
2159
+ }
2160
+ }
2161
+ }
2162
+ }
2026
2163
  if (fileNames.has("package.json")) {
2027
2164
  const pkgRes = await fetch(`https://${host}/api/v4/projects/${encodedPath}/repository/files/package.json/raw?ref=HEAD`, {
2028
2165
  headers: { "User-Agent": "LynxPrompt-CLI" }
@@ -2033,9 +2170,17 @@ async function detectFromGitLabApi(repoUrl) {
2033
2170
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
2034
2171
  if (allDeps["next"]) detected.stack.push("nextjs");
2035
2172
  if (allDeps["react"]) detected.stack.push("react");
2173
+ if (allDeps["vue"]) detected.stack.push("vue");
2174
+ if (allDeps["svelte"]) detected.stack.push("svelte");
2175
+ if (allDeps["express"]) detected.stack.push("express");
2176
+ if (allDeps["fastify"]) detected.stack.push("fastify");
2036
2177
  if (allDeps["typescript"]) detected.stack.push("typescript");
2037
2178
  if (allDeps["tailwindcss"]) detected.stack.push("tailwind");
2038
- if (detected.stack.length === 0) {
2179
+ if (allDeps["prisma"]) detected.stack.push("prisma");
2180
+ if (allDeps["vitest"]) detected.testFramework = "vitest";
2181
+ else if (allDeps["jest"]) detected.testFramework = "jest";
2182
+ else if (allDeps["@playwright/test"]) detected.testFramework = "playwright";
2183
+ if (detected.stack.length === 0 || detected.stack.length === 1 && detected.stack[0] === "typescript") {
2039
2184
  detected.stack.unshift("javascript");
2040
2185
  }
2041
2186
  if (pkg.scripts) {
@@ -2044,15 +2189,57 @@ async function detectFromGitLabApi(repoUrl) {
2044
2189
  if (pkg.scripts.lint) detected.commands.lint = "npm run lint";
2045
2190
  if (pkg.scripts.dev) detected.commands.dev = "npm run dev";
2046
2191
  }
2192
+ if (allDeps["pg"] || allDeps["postgres"]) {
2193
+ if (!detected.databases.includes("postgresql")) detected.databases.push("postgresql");
2194
+ }
2195
+ if (allDeps["better-sqlite3"] || allDeps["sqlite3"]) {
2196
+ if (!detected.databases.includes("sqlite")) detected.databases.push("sqlite");
2197
+ }
2198
+ if (allDeps["mongodb"] || allDeps["mongoose"]) {
2199
+ if (!detected.databases.includes("mongodb")) detected.databases.push("mongodb");
2200
+ }
2201
+ if (allDeps["redis"] || allDeps["ioredis"]) {
2202
+ if (!detected.databases.includes("redis")) detected.databases.push("redis");
2203
+ }
2047
2204
  } catch {
2048
2205
  }
2049
2206
  }
2050
2207
  }
2051
- if (fileNames.has("pyproject.toml") || fileNames.has("requirements.txt")) detected.stack.push("python");
2052
- if (fileNames.has("cargo.toml")) detected.stack.push("rust");
2053
- if (fileNames.has("go.mod")) detected.stack.push("go");
2054
- if (fileNames.has("dockerfile")) detected.hasDocker = true;
2055
- if (fileNames.has(".gitlab-ci.yml")) detected.cicd = "gitlab_ci";
2208
+ if (fileNames.has("pyproject.toml")) {
2209
+ detected.stack.push("python");
2210
+ const pyRes = await fetch(`https://${host}/api/v4/projects/${encodedPath}/repository/files/pyproject.toml/raw?ref=HEAD`, {
2211
+ headers: { "User-Agent": "LynxPrompt-CLI" }
2212
+ });
2213
+ if (pyRes.ok) {
2214
+ try {
2215
+ const content = (await pyRes.text()).toLowerCase();
2216
+ if (content.includes("fastapi")) detected.stack.push("fastapi");
2217
+ if (content.includes("django")) detected.stack.push("django");
2218
+ if (content.includes("flask")) detected.stack.push("flask");
2219
+ if (content.includes("sqlalchemy")) detected.stack.push("sqlalchemy");
2220
+ if (content.includes("pytest")) detected.testFramework = "pytest";
2221
+ if (content.includes("asyncpg") || content.includes("psycopg")) {
2222
+ if (!detected.databases.includes("postgresql")) detected.databases.push("postgresql");
2223
+ }
2224
+ if (content.includes("aiosqlite") || content.includes("sqlite")) {
2225
+ if (!detected.databases.includes("sqlite")) detected.databases.push("sqlite");
2226
+ }
2227
+ } catch {
2228
+ }
2229
+ }
2230
+ } else if (fileNames.has("requirements.txt")) {
2231
+ detected.stack.push("python");
2232
+ }
2233
+ if (fileNames.has("cargo.toml")) {
2234
+ detected.stack.push("rust");
2235
+ detected.commands.build = "cargo build";
2236
+ detected.commands.test = "cargo test";
2237
+ }
2238
+ if (fileNames.has("go.mod")) {
2239
+ detected.stack.push("go");
2240
+ detected.commands.build = "go build";
2241
+ detected.commands.test = "go test ./...";
2242
+ }
2056
2243
  return detected;
2057
2244
  } catch {
2058
2245
  return null;
@@ -3400,7 +3587,7 @@ function generateYamlConfig(options, platform2) {
3400
3587
 
3401
3588
  // src/commands/wizard.ts
3402
3589
  var DRAFTS_DIR = ".lynxprompt/drafts";
3403
- var CLI_VERSION = "1.2.11";
3590
+ var CLI_VERSION = "1.2.13";
3404
3591
  async function saveDraftLocally(name, config2, stepReached) {
3405
3592
  const draftsPath = join4(process.cwd(), DRAFTS_DIR);
3406
3593
  await mkdir3(draftsPath, { recursive: true });
@@ -3670,19 +3857,42 @@ var CICD_OPTIONS = [
3670
3857
  { id: "drone", label: "Drone", icon: "\u{1F681}" },
3671
3858
  { id: "buildkite", label: "Buildkite", icon: "\u{1F9F1}" }
3672
3859
  ];
3673
- var DEPLOYMENT_TARGETS = [
3860
+ var CLOUD_TARGETS = [
3674
3861
  { id: "vercel", label: "Vercel", icon: "\u25B2 " },
3675
3862
  { id: "netlify", label: "Netlify", icon: "\u{1F310}" },
3676
- { id: "aws", label: "AWS", icon: "\u2601\uFE0F " },
3677
- { id: "gcp", label: "Google Cloud", icon: "\u{1F308}" },
3678
- { id: "azure", label: "Azure", icon: "\u{1F537}" },
3679
- { id: "docker", label: "Docker", icon: "\u{1F433}" },
3680
- { id: "kubernetes", label: "Kubernetes", icon: "\u2638\uFE0F " },
3681
- { id: "heroku", label: "Heroku", icon: "\u{1F7E3}" },
3682
- { id: "digitalocean", label: "DigitalOcean", icon: "\u{1F535}" },
3863
+ { id: "cloudflare_pages", label: "Cloudflare Pages", icon: "\u{1F536}" },
3864
+ { id: "cloudflare_workers", label: "Cloudflare Workers", icon: "\u{1F536}" },
3865
+ { id: "aws_lambda", label: "AWS Lambda", icon: "\u2601\uFE0F " },
3866
+ { id: "aws_ecs", label: "AWS ECS", icon: "\u2601\uFE0F " },
3867
+ { id: "aws_ec2", label: "AWS EC2", icon: "\u2601\uFE0F " },
3868
+ { id: "gcp_cloudrun", label: "GCP Cloud Run", icon: "\u{1F308}" },
3869
+ { id: "gcp_appengine", label: "GCP App Engine", icon: "\u{1F308}" },
3870
+ { id: "azure_appservice", label: "Azure App Service", icon: "\u{1F537}" },
3871
+ { id: "azure_functions", label: "Azure Functions", icon: "\u{1F537}" },
3683
3872
  { id: "railway", label: "Railway", icon: "\u{1F682}" },
3873
+ { id: "render", label: "Render", icon: "\u{1F3A8}" },
3684
3874
  { id: "fly", label: "Fly.io", icon: "\u2708\uFE0F " },
3685
- { id: "cloudflare", label: "Cloudflare", icon: "\u{1F536}" }
3875
+ { id: "heroku", label: "Heroku", icon: "\u{1F7E3}" },
3876
+ { id: "digitalocean_app", label: "DigitalOcean App Platform", icon: "\u{1F535}" },
3877
+ { id: "deno_deploy", label: "Deno Deploy", icon: "\u{1F995}" }
3878
+ ];
3879
+ var SELF_HOSTED_TARGETS = [
3880
+ { id: "docker", label: "Docker", icon: "\u{1F433}" },
3881
+ { id: "docker_compose", label: "Docker Compose", icon: "\u{1F433}" },
3882
+ { id: "kubernetes", label: "Kubernetes", icon: "\u2638\uFE0F " },
3883
+ { id: "k3s", label: "K3s", icon: "\u2638\uFE0F " },
3884
+ { id: "podman", label: "Podman", icon: "\u{1F9AD}" },
3885
+ { id: "bare_metal", label: "Bare Metal", icon: "\u{1F5A5}\uFE0F " },
3886
+ { id: "vm", label: "Virtual Machine", icon: "\u{1F4BB}" },
3887
+ { id: "proxmox", label: "Proxmox", icon: "\u{1F537}" },
3888
+ { id: "unraid", label: "Unraid", icon: "\u{1F7E0}" },
3889
+ { id: "truenas", label: "TrueNAS", icon: "\u{1F535}" },
3890
+ { id: "synology", label: "Synology NAS", icon: "\u{1F4C1}" },
3891
+ { id: "coolify", label: "Coolify", icon: "\u2744\uFE0F " },
3892
+ { id: "dokku", label: "Dokku", icon: "\u{1F433}" },
3893
+ { id: "caprover", label: "CapRover", icon: "\u{1F6A2}" },
3894
+ { id: "portainer", label: "Portainer", icon: "\u{1F40B}" },
3895
+ { id: "rancher", label: "Rancher", icon: "\u{1F404}" }
3686
3896
  ];
3687
3897
  var CONTAINER_REGISTRIES = [
3688
3898
  { id: "dockerhub", label: "Docker Hub", icon: "\u{1F433}" },
@@ -4496,13 +4706,24 @@ async function runWizardWithDraftProtection(options) {
4496
4706
  console.log();
4497
4707
  console.log(chalk7.white(` Name: ${detected2.name || "unknown"}`));
4498
4708
  if (detected2.description) console.log(chalk7.gray(` Description: ${detected2.description}`));
4709
+ console.log(chalk7.white(` Type: ${detected2.isOpenSource ? "Open Source" : detected2.type}`));
4499
4710
  console.log(chalk7.white(` Stack: ${detected2.stack.join(", ") || "none detected"}`));
4500
- console.log(chalk7.white(` Type: ${detected2.type}`));
4711
+ if (detected2.databases && detected2.databases.length > 0) {
4712
+ console.log(chalk7.white(` Databases: ${detected2.databases.join(", ")}`));
4713
+ }
4501
4714
  if (detected2.packageManager) console.log(chalk7.white(` Package Manager: ${detected2.packageManager}`));
4502
- if (detected2.repoHost) console.log(chalk7.white(` Repository Host: ${detected2.repoHost}`));
4503
- if (detected2.license) console.log(chalk7.white(` License: ${detected2.license}`));
4504
- if (detected2.cicd) console.log(chalk7.white(` CI/CD: ${detected2.cicd}`));
4505
- if (detected2.hasDocker) console.log(chalk7.white(` Docker: yes`));
4715
+ if (detected2.repoHost) console.log(chalk7.white(` Host: ${detected2.repoHost}`));
4716
+ if (detected2.license) console.log(chalk7.white(` License: ${detected2.license.toUpperCase()}`));
4717
+ if (detected2.cicd) console.log(chalk7.white(` CI/CD: ${detected2.cicd.replace("_", " ")}`));
4718
+ if (detected2.hasDocker) {
4719
+ const dockerInfo = detected2.containerRegistry ? `detected (registry: ${detected2.containerRegistry})` : "detected";
4720
+ console.log(chalk7.white(` Docker: ${dockerInfo}`));
4721
+ }
4722
+ if (detected2.testFramework) console.log(chalk7.white(` Test Framework: ${detected2.testFramework}`));
4723
+ if (detected2.existingFiles && detected2.existingFiles.length > 0) {
4724
+ const filesDisplay = detected2.existingFiles.length > 3 ? `${detected2.existingFiles.slice(0, 3).join(", ")}... (+${detected2.existingFiles.length - 3})` : detected2.existingFiles.join(", ");
4725
+ console.log(chalk7.white(` Static files found: ${detected2.existingFiles.length} (${filesDisplay})`));
4726
+ }
4506
4727
  if (detected2.commands) {
4507
4728
  console.log(chalk7.white(` Commands:`));
4508
4729
  if (detected2.commands.build) console.log(chalk7.gray(` build: ${detected2.commands.build}`));
@@ -4633,7 +4854,22 @@ async function runWizardWithDraftProtection(options) {
4633
4854
  chalk7.green("\u2713 Remote project detected")
4634
4855
  ];
4635
4856
  if (detected.name) detectedInfo.push(chalk7.gray(` Name: ${detected.name}`));
4857
+ if (detected.isOpenSource) detectedInfo.push(chalk7.gray(` Type: Open Source`));
4636
4858
  if (detected.stack.length > 0) detectedInfo.push(chalk7.gray(` Stack: ${detected.stack.join(", ")}`));
4859
+ if (detected.databases && detected.databases.length > 0) {
4860
+ detectedInfo.push(chalk7.gray(` Databases: ${detected.databases.join(", ")}`));
4861
+ }
4862
+ if (detected.license) detectedInfo.push(chalk7.gray(` License: ${detected.license.toUpperCase()}`));
4863
+ if (detected.repoHost) detectedInfo.push(chalk7.gray(` Host: ${detected.repoHost}`));
4864
+ if (detected.cicd) detectedInfo.push(chalk7.gray(` CI/CD: ${detected.cicd.replace("_", " ")}`));
4865
+ if (detected.hasDocker) {
4866
+ const dockerInfo = detected.containerRegistry ? `detected (registry: ${detected.containerRegistry})` : "detected";
4867
+ detectedInfo.push(chalk7.gray(` Docker: ${dockerInfo}`));
4868
+ }
4869
+ if (detected.existingFiles && detected.existingFiles.length > 0) {
4870
+ const filesDisplay = detected.existingFiles.length > 3 ? `${detected.existingFiles.slice(0, 3).join(", ")}...` : detected.existingFiles.join(", ");
4871
+ detectedInfo.push(chalk7.gray(` Static files found: ${detected.existingFiles.length} (${filesDisplay})`));
4872
+ }
4637
4873
  if (detected.repoUrl) detectedInfo.push(chalk7.gray(` Source: ${detected.repoUrl}`));
4638
4874
  printBox(detectedInfo, chalk7.gray);
4639
4875
  console.log();
@@ -5233,14 +5469,14 @@ async function runInteractiveWizard(options, detected, userTier) {
5233
5469
  name: "changelogTool",
5234
5470
  message: chalk7.white("Changelog management:"),
5235
5471
  choices: [
5236
- { title: "Manual", value: "manual" },
5237
- { title: "Conventional Changelog", value: "conventional_changelog" },
5238
- { title: "Release Please", value: "release_please" },
5239
- { title: "Semantic Release", value: "semantic_release" },
5240
- { title: "Changesets", value: "changesets" },
5241
- { title: "GitHub Releases", value: "github_releases" },
5242
- { title: "Keep a Changelog", value: "keep_a_changelog" },
5243
- { title: "Other", value: "other" }
5472
+ { title: "Manual - Write CHANGELOG.md by hand", value: "manual" },
5473
+ { title: "Conventional Changelog - Auto-generate from commit messages", value: "conventional_changelog" },
5474
+ { title: "Release Please - Google's automated release management", value: "release_please" },
5475
+ { title: "Semantic Release - Fully automated versioning & publishing", value: "semantic_release" },
5476
+ { title: "Changesets - Monorepo-friendly version management", value: "changesets" },
5477
+ { title: "GitHub Releases - Use GitHub's built-in release notes", value: "github_releases" },
5478
+ { title: "Keep a Changelog - Manual following keepachangelog.com format", value: "keep_a_changelog" },
5479
+ { title: "Other - Custom changelog tooling", value: "other" }
5244
5480
  ],
5245
5481
  initial: 0
5246
5482
  }, promptConfig);
@@ -5260,13 +5496,15 @@ async function runInteractiveWizard(options, detected, userTier) {
5260
5496
  name: "branchStrategy",
5261
5497
  message: chalk7.white("Branch strategy:"),
5262
5498
  choices: [
5499
+ { title: "\u{1F3AE} None (toy project) - No branching, commit directly to main", value: "none" },
5263
5500
  { title: "\u{1F30A} GitHub Flow - Simple: main + feature branches", value: "github_flow" },
5264
5501
  { title: "\u{1F333} Gitflow - develop, feature, release, hotfix branches", value: "gitflow" },
5265
5502
  { title: "\u{1F682} Trunk-Based - Short-lived branches, continuous integration", value: "trunk_based" },
5266
5503
  { title: "\u{1F98A} GitLab Flow - Environment branches (staging, production)", value: "gitlab_flow" },
5267
5504
  { title: "\u{1F680} Release Flow - main + release branches", value: "release_flow" }
5268
5505
  ],
5269
- initial: 0
5506
+ initial: 1
5507
+ // Default to GitHub Flow (index 1 after adding "none")
5270
5508
  }, promptConfig);
5271
5509
  answers.branchStrategy = branchStrategyResponse.branchStrategy || "github_flow";
5272
5510
  const defaultBranchResponse = await prompts3({
@@ -5282,18 +5520,7 @@ async function runInteractiveWizard(options, detected, userTier) {
5282
5520
  initial: 0
5283
5521
  }, promptConfig);
5284
5522
  answers.defaultBranch = defaultBranchResponse.defaultBranch || "main";
5285
- const commitWorkflowResponse = await prompts3({
5286
- type: "select",
5287
- name: "commitWorkflow",
5288
- message: chalk7.white("Commit workflow preference:"),
5289
- choices: [
5290
- { title: "\u{1F500} Branch + PR - Create branches and open pull requests", value: "branch_pr" },
5291
- { title: "\u2B06\uFE0F Direct to main - Commit directly to main/master branch", value: "direct_main" }
5292
- ],
5293
- initial: 0
5294
- // Default to branch + PR
5295
- }, promptConfig);
5296
- answers.commitWorkflow = commitWorkflowResponse.commitWorkflow || "branch_pr";
5523
+ answers.commitWorkflow = answers.branchStrategy === "none" ? "direct_main" : "branch_pr";
5297
5524
  const cicdChoices = [
5298
5525
  { title: chalk7.gray("\u23ED Skip"), value: "" },
5299
5526
  ...CICD_OPTIONS.map((c) => ({
@@ -5310,27 +5537,61 @@ async function runInteractiveWizard(options, detected, userTier) {
5310
5537
  initial: detectedCicdIndex > 0 ? detectedCicdIndex : 0
5311
5538
  }, promptConfig);
5312
5539
  answers.cicd = cicdResponse.cicd || "";
5313
- const deployChoices = sortSelectedFirst(DEPLOYMENT_TARGETS.map((t) => ({
5314
- title: t.id === "docker" && detected?.hasDocker ? `${t.icon}${t.label} ${chalk7.green("(detected)")}` : `${t.icon}${t.label}`,
5315
- selected: t.id === "docker" && detected?.hasDocker,
5316
- value: t.id
5317
- })));
5318
- const deployResponse = await prompts3({
5319
- type: "autocompleteMultiselect",
5320
- name: "deploymentTargets",
5321
- message: chalk7.white("Deployment targets (type to search):"),
5322
- choices: deployChoices,
5323
- hint: chalk7.gray("type to filter \u2022 space select \u2022 enter confirm"),
5324
- instructions: false
5540
+ const deployTypeResponse = await prompts3({
5541
+ type: "select",
5542
+ name: "deployType",
5543
+ message: chalk7.white("Deployment environment:"),
5544
+ choices: [
5545
+ { title: "\u2601\uFE0F Cloud only - PaaS, serverless, managed services", value: "cloud" },
5546
+ { title: "\u{1F3E0} Self-hosted only - On-premise, homelab, VPS", value: "self_hosted" },
5547
+ { title: "\u{1F504} Both cloud and self-hosted", value: "both" },
5548
+ { title: chalk7.gray("\u23ED Skip"), value: "skip" }
5549
+ ],
5550
+ initial: 0
5325
5551
  }, promptConfig);
5326
- answers.deploymentTargets = deployResponse.deploymentTargets || [];
5327
- const dockerSelected = (answers.deploymentTargets || []).includes("docker");
5552
+ const deployType = deployTypeResponse.deployType || "skip";
5553
+ let allDeployTargets = [];
5554
+ if (deployType === "cloud" || deployType === "both") {
5555
+ const cloudChoices = CLOUD_TARGETS.map((t) => ({
5556
+ title: `${t.icon}${t.label}`,
5557
+ value: t.id
5558
+ }));
5559
+ const cloudResponse = await prompts3({
5560
+ type: "autocompleteMultiselect",
5561
+ name: "cloudTargets",
5562
+ message: chalk7.white("Cloud deployment targets (type to search):"),
5563
+ choices: cloudChoices,
5564
+ hint: chalk7.gray("type to filter \u2022 space select \u2022 enter confirm"),
5565
+ instructions: false
5566
+ }, promptConfig);
5567
+ allDeployTargets = [...cloudResponse.cloudTargets || []];
5568
+ }
5569
+ if (deployType === "self_hosted" || deployType === "both") {
5570
+ const selfHostedChoices = sortSelectedFirst(SELF_HOSTED_TARGETS.map((t) => ({
5571
+ title: t.id === "docker" && detected?.hasDocker ? `${t.icon}${t.label} ${chalk7.green("(detected)")}` : `${t.icon}${t.label}`,
5572
+ selected: t.id === "docker" && detected?.hasDocker,
5573
+ value: t.id
5574
+ })));
5575
+ const selfHostedResponse = await prompts3({
5576
+ type: "autocompleteMultiselect",
5577
+ name: "selfHostedTargets",
5578
+ message: chalk7.white("Self-hosted deployment targets (type to search):"),
5579
+ choices: selfHostedChoices,
5580
+ hint: chalk7.gray("type to filter \u2022 space select \u2022 enter confirm"),
5581
+ instructions: false
5582
+ }, promptConfig);
5583
+ allDeployTargets = [...allDeployTargets, ...selfHostedResponse.selfHostedTargets || []];
5584
+ }
5585
+ answers.deploymentTargets = allDeployTargets;
5586
+ const dockerSelected = (answers.deploymentTargets || []).some(
5587
+ (t) => ["docker", "docker_compose", "kubernetes", "k3s", "podman"].includes(t)
5588
+ ) || detected?.hasDocker;
5328
5589
  const containerResponse = await prompts3({
5329
5590
  type: "toggle",
5330
5591
  name: "buildContainer",
5331
5592
  message: chalk7.white("Build container images (Docker)?"),
5332
5593
  initial: dockerSelected,
5333
- // Default Yes if Docker selected
5594
+ // Default Yes if container platform selected
5334
5595
  active: "Yes",
5335
5596
  inactive: "No"
5336
5597
  }, promptConfig);
@@ -8597,7 +8858,7 @@ function handleError2(error) {
8597
8858
  }
8598
8859
 
8599
8860
  // src/index.ts
8600
- var CLI_VERSION2 = "1.2.11";
8861
+ var CLI_VERSION2 = "1.2.13";
8601
8862
  var program = new Command();
8602
8863
  program.name("lynxprompt").description("CLI for LynxPrompt - Generate AI IDE configuration files").version(CLI_VERSION2);
8603
8864
  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);