asyq 8.0.12 → 8.0.15

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/asyq.js CHANGED
@@ -20,7 +20,9 @@ var IGNORE_DIRS = /* @__PURE__ */ new Set([
20
20
  "out",
21
21
  "coverage",
22
22
  ".turbo",
23
- ".cache"
23
+ ".cache",
24
+ ".vercel",
25
+ ".netlify"
24
26
  ]);
25
27
  var ENV_KEY_RE_STRICT = /^[A-Z][A-Z0-9_]*$/;
26
28
  var ENV_KEY_RE_LOOSE = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -43,8 +45,29 @@ function scanProjectForEnvKeys(opts) {
43
45
  const keys = /* @__PURE__ */ new Set();
44
46
  const contexts = {};
45
47
  let filesScanned = 0;
48
+ const seenInCode = /* @__PURE__ */ new Set();
46
49
  walk(root);
47
- return { keys, filesScanned, contexts };
50
+ const finalKeys = /* @__PURE__ */ new Set();
51
+ const seenInCodeUpper = new Set([...seenInCode].map((k) => k.toUpperCase()));
52
+ for (const key of keys) {
53
+ if (seenInCodeUpper.has(key.toUpperCase())) {
54
+ finalKeys.add(key);
55
+ }
56
+ }
57
+ for (const codeKey of seenInCode) {
58
+ const upper = codeKey.toUpperCase();
59
+ let found = false;
60
+ for (const key of finalKeys) {
61
+ if (key.toUpperCase() === upper) {
62
+ found = true;
63
+ break;
64
+ }
65
+ }
66
+ if (!found && keyOk(codeKey)) {
67
+ finalKeys.add(codeKey);
68
+ }
69
+ }
70
+ return { keys: finalKeys, filesScanned, contexts };
48
71
  function addCtx(key, relFile, line, snippet) {
49
72
  if (!contexts[key]) contexts[key] = [];
50
73
  if (contexts[key].length >= maxCtx) return;
@@ -77,7 +100,7 @@ function scanProjectForEnvKeys(opts) {
77
100
  if (isEnvFile) {
78
101
  extractFromEnvFile(content, rel, keys, addCtx, keyOk);
79
102
  } else {
80
- extractFromCode(content, rel, keys, addCtx, keyOk);
103
+ extractFromCode(content, rel, keys, seenInCode, addCtx, keyOk);
81
104
  }
82
105
  }
83
106
  }
@@ -86,6 +109,7 @@ function extractFromEnvFile(text, relFile, keys, addCtx, keyOk) {
86
109
  const lines = text.split(/\r?\n/);
87
110
  for (let i = 0; i < lines.length; i++) {
88
111
  const ln = lines[i];
112
+ if (!ln.trim() || ln.trim().startsWith("#")) continue;
89
113
  const m = ln.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/);
90
114
  if (!m) continue;
91
115
  const k = m[1];
@@ -94,20 +118,23 @@ function extractFromEnvFile(text, relFile, keys, addCtx, keyOk) {
94
118
  addCtx(k, relFile, i + 1, ln);
95
119
  }
96
120
  }
97
- function extractFromCode(text, relFile, keys, addCtx, keyOk) {
121
+ function extractFromCode(text, relFile, keys, seenInCode, addCtx, keyOk) {
98
122
  const lines = text.split(/\r?\n/);
99
123
  const patterns = [
100
124
  // process.env.KEY or process?.env?.KEY
101
- /\bprocess(?:\?\.)?\.env(?:\?\.)?\.([A-Za-z_][A-Za-z0-9_]*)\b/g,
102
- // process.env["KEY"] or process.env['KEY']
103
- /\bprocess(?:\?\.)?\.env\[\s*["']([A-Za-z_][A-Za-z0-9_]*)["']\s*\]/g,
104
- // import.meta.env.KEY
105
- /\bimport\.meta\.env\.([A-Za-z_][A-Za-z0-9_]*)\b/g,
125
+ /\bprocess(?:\?\.|\.)env(?:\?\.|\.)([A-Za-z_][A-Za-z0-9_]*)\b/gi,
126
+ // process.env["KEY"] or process?.env?.["KEY"]
127
+ /\bprocess(?:\?\.|\.)env\[\s*["']([A-Za-z_][A-Za-z0-9_]*)["']\s*\]/gi,
128
+ // import.meta.env.KEY (Vite, etc.)
129
+ /\bimport\.meta\.env\.([A-Za-z_][A-Za-z0-9_]*)\b/gi,
106
130
  // Deno.env.get("KEY")
107
- /\bDeno\.env\.get\(\s*["']([A-Za-z_][A-Za-z0-9_]*)["']\s*\)/g
131
+ /\bDeno\.env\.get\(\s*["']([A-Za-z_][A-Za-z0-9_]*)["']\s*\)/gi,
132
+ // Bun.env.KEY
133
+ /\bBun\.env\.([A-Za-z_][A-Za-z0-9_]*)\b/gi
108
134
  ];
109
135
  for (let i = 0; i < lines.length; i++) {
110
136
  const ln = lines[i];
137
+ if (ln.trim().startsWith("//") || ln.trim().startsWith("#")) continue;
111
138
  for (const re of patterns) {
112
139
  re.lastIndex = 0;
113
140
  let match;
@@ -115,6 +142,7 @@ function extractFromCode(text, relFile, keys, addCtx, keyOk) {
115
142
  const k = match[1];
116
143
  if (!keyOk(k)) continue;
117
144
  keys.add(k);
145
+ seenInCode.add(k);
118
146
  addCtx(k, relFile, i + 1, ln);
119
147
  }
120
148
  }
package/dist/asyq.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/asyq.ts","../src/scan.ts","../src/ai.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\n\nimport { scanProjectForEnvKeys } from \"./scan.js\";\nimport { generateEnvDocsWithOpenAI } from \"./ai.js\";\n\nconst MODELS = [\n \"gpt-5\",\n \"gpt-5-mini\",\n \"gpt-5-nano\",\n \"gpt-4.1\",\n \"gpt-4.1-mini\",\n \"gpt-4.1-nano\",\n] as const;\n\ntype ModelName = (typeof MODELS)[number];\n\nfunction getPackageVersion(): string {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const pkgPath = path.resolve(__dirname, \"../package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n return pkg.version ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction fail(message: string): never {\n p.outro(pc.red(message));\n process.exit(1);\n}\n\nasync function pickMode(): Promise<\"default\" | \"ai\"> {\n const mode = await p.select({\n message: \"How would you like to generate .env.example?\",\n options: [\n { label: \"Default (fast)\", value: \"default\" },\n { label: \"AI-assisted (adds descriptions)\", value: \"ai\" },\n ],\n });\n\n if (p.isCancel(mode)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return mode as \"default\" | \"ai\";\n}\n\nasync function pickModel(): Promise<ModelName> {\n const model = await p.select({\n message: \"Select an AI model\",\n initialValue: \"gpt-4.1-mini\",\n options: MODELS.map((m) => ({ label: m, value: m })),\n });\n\n if (p.isCancel(model)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return model as ModelName;\n}\n\nasync function getApiKey(): Promise<string> {\n const envKey = process.env.OPENAI_API_KEY?.trim();\n if (envKey) return envKey;\n\n const key = await p.password({\n message: \"Enter OpenAI API key (not saved)\",\n validate: (v) =>\n v.trim().length > 0 ? undefined : \"API key cannot be empty\",\n });\n\n if (p.isCancel(key)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return key.trim();\n}\n\n/* ----------------------------- Monorepo support ---------------------------- */\n\nfunction readWorkspaceGlobs(rootAbs: string): string[] {\n const globs: string[] = [];\n\n // pnpm-workspace.yaml\n const pnpmWs = path.join(rootAbs, \"pnpm-workspace.yaml\");\n if (fs.existsSync(pnpmWs)) {\n const txt = fs.readFileSync(pnpmWs, \"utf8\");\n for (const line of txt.split(/\\r?\\n/)) {\n const m = line.match(/^\\s*-\\s*[\"']?([^\"']+)[\"']?\\s*$/);\n if (m) globs.push(m[1].trim());\n }\n }\n\n // package.json workspaces\n const pkgPath = path.join(rootAbs, \"package.json\");\n if (fs.existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n const ws = pkg?.workspaces;\n if (Array.isArray(ws)) globs.push(...ws);\n if (ws && Array.isArray(ws.packages)) globs.push(...ws.packages);\n } catch {\n // ignore\n }\n }\n\n return [...new Set(globs)].filter(Boolean);\n}\n\nfunction expandSimpleGlob(rootAbs: string, pattern: string): string[] {\n const norm = pattern.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n\n if (!norm.includes(\"*\")) {\n const abs = path.join(rootAbs, norm);\n return fs.existsSync(abs) && fs.statSync(abs).isDirectory() ? [norm] : [];\n }\n\n const m = norm.match(/^([^*]+)\\/\\*$/);\n if (!m) return [];\n\n const baseRel = m[1].replace(/\\/+$/, \"\");\n const baseAbs = path.join(rootAbs, baseRel);\n if (!fs.existsSync(baseAbs) || !fs.statSync(baseAbs).isDirectory()) return [];\n\n const out: string[] = [];\n for (const name of fs.readdirSync(baseAbs)) {\n const rel = `${baseRel}/${name}`;\n const abs = path.join(rootAbs, rel);\n if (!fs.statSync(abs).isDirectory()) continue;\n if (fs.existsSync(path.join(abs, \"package.json\"))) out.push(rel);\n }\n return out;\n}\n\nfunction detectWorkspaces(rootAbs: string): string[] {\n const globs = readWorkspaceGlobs(rootAbs);\n const found = new Set<string>();\n\n for (const g of globs) {\n for (const rel of expandSimpleGlob(rootAbs, g)) found.add(rel);\n }\n\n // Turbo-style fallback if no globs detected\n if (found.size === 0) {\n for (const base of [\"apps\", \"packages\"]) {\n const baseAbs = path.join(rootAbs, base);\n if (!fs.existsSync(baseAbs) || !fs.statSync(baseAbs).isDirectory())\n continue;\n for (const name of fs.readdirSync(baseAbs)) {\n const rel = `${base}/${name}`;\n const abs = path.join(rootAbs, rel);\n if (!fs.statSync(abs).isDirectory()) continue;\n if (fs.existsSync(path.join(abs, \"package.json\"))) found.add(rel);\n }\n }\n }\n\n return [...found].sort((a, b) => a.localeCompare(b));\n}\n\n/* -------------------------------------------------------------------------- */\n\nconst program = new Command();\n\nprogram\n .name(\"asyq\")\n .description(\"Generate .env.example by scanning your project for env usage\")\n .version(`v${getPackageVersion()}`);\n\nprogram\n .command(\"init\")\n .description(\"Scan project and generate .env.example\")\n .option(\"--root <dir>\", \"Project root to scan\", \".\")\n .option(\"--out <file>\", \"Output file name\", \".env.example\")\n .option(\"--force\", \"Overwrite output if it exists\")\n .option(\n \"--include-lowercase\",\n \"Include lowercase/mixed-case keys (not recommended)\"\n )\n .option(\"--debug\", \"Print scan diagnostics\")\n .option(\"--monorepo\", \"Generate .env.example for root + each workspace\")\n .action(async (opts) => {\n p.intro(pc.cyan(`Asyq v${getPackageVersion()}`));\n\n const rootAbs = path.resolve(process.cwd(), opts.root);\n const outName = String(opts.out || \".env.example\");\n\n const mode = await pickMode();\n const model: ModelName | null = mode === \"ai\" ? await pickModel() : null;\n\n const targets: { label: string; dirAbs: string }[] = [\n { label: \"root\", dirAbs: rootAbs },\n ];\n\n if (opts.monorepo) {\n const workspaces = detectWorkspaces(rootAbs);\n for (const rel of workspaces) {\n targets.push({ label: rel, dirAbs: path.join(rootAbs, rel) });\n }\n }\n\n // API key once for all targets (AI mode)\n let apiKey = \"\";\n if (mode === \"ai\" && model) {\n apiKey = await getApiKey();\n if (!apiKey) {\n fail(\"OpenAI API key is required for AI-assisted mode.\");\n }\n }\n\n const results: Array<{\n target: string;\n outRel: string;\n keys: number;\n files: number;\n }> = [];\n\n for (const t of targets) {\n const outFileAbs = path.join(t.dirAbs, outName);\n const outRelFromRoot =\n path.relative(rootAbs, outFileAbs).replace(/\\\\/g, \"/\") || outName;\n\n if (fs.existsSync(outFileAbs) && !opts.force) {\n fail(\n `Output already exists: ${outRelFromRoot}. Use --force to overwrite.`\n );\n }\n\n const s = p.spinner();\n s.start(`Scanning ${t.label}`);\n\n const res = scanProjectForEnvKeys({\n rootDir: t.dirAbs,\n includeLowercase: !!opts.includeLowercase,\n });\n\n s.stop(\n `Scanned ${t.label} (${res.filesScanned} files, ${res.keys.size} keys)`\n );\n\n if (opts.debug) {\n p.note(\n [\n `dir: ${t.dirAbs}`,\n `files scanned: ${res.filesScanned}`,\n `keys found: ${res.keys.size}`,\n ].join(\"\\n\"),\n `${t.label} diagnostics`\n );\n }\n\n if (res.keys.size === 0) {\n results.push({\n target: t.label,\n outRel: outRelFromRoot,\n keys: 0,\n files: res.filesScanned,\n });\n continue;\n }\n\n const keys = [...res.keys].sort((a, b) => a.localeCompare(b));\n\n let content = keys.map((k) => `${k}=`).join(\"\\n\") + \"\\n\";\n\n if (mode === \"ai\" && model) {\n const aiSpinner = p.spinner();\n aiSpinner.start(`Generating AI docs for ${t.label}`);\n\n try {\n const docs = await generateEnvDocsWithOpenAI({\n apiKey,\n model,\n projectHint:\n \"Write practical guidance for developers setting env vars.\",\n contexts: res.contexts,\n keys,\n });\n\n aiSpinner.stop(`AI docs generated for ${t.label}`);\n\n const byKey = new Map(docs.map((d) => [d.key, d]));\n\n content =\n keys\n .map((k) => {\n const d = byKey.get(k);\n if (!d) return `${k}=\\n`;\n\n const secretNote = d.is_secret\n ? \"Secret value. Do not commit.\"\n : \"Non-secret value (verify before committing).\";\n\n return [\n `# ${d.key}`,\n `# ${d.description}`,\n `# Where to get it: ${d.where_to_get}`,\n `# ${secretNote}`,\n `${d.key}=${d.example_value || \"\"}`,\n \"\",\n ].join(\"\\n\");\n })\n .join(\"\\n\")\n .trimEnd() + \"\\n\";\n } catch (e: any) {\n aiSpinner.stop(`AI generation failed for ${t.label}`);\n fail(e?.message ?? String(e));\n }\n }\n\n fs.writeFileSync(outFileAbs, content, \"utf8\");\n\n results.push({\n target: t.label,\n outRel: outRelFromRoot,\n keys: keys.length,\n files: res.filesScanned,\n });\n }\n\n // Summary table\n const summary = results\n .map(\n (r) =>\n `${pc.cyan(r.target.padEnd(20))} ${pc.green(\n String(r.keys).padStart(3)\n )} keys → ${pc.dim(r.outRel)}`\n )\n .join(\"\\n\");\n\n p.note(summary, \"Generated\");\n p.outro(pc.green(\"✓ All done!\"));\n });\n\nprogram.parse(process.argv);\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport type ScanOptions = {\n rootDir: string;\n includeLowercase?: boolean;\n maxContextPerKey?: number;\n};\n\nexport type KeyContext = { file: string; line: number; snippet: string };\n\nexport type ScanResult = {\n keys: Set<string>;\n filesScanned: number;\n contexts: Record<string, KeyContext[]>;\n};\n\nconst IGNORE_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \"out\",\n \"coverage\",\n \".turbo\",\n \".cache\",\n]);\n\n// ENV VARS MUST BE EXPLICIT + UPPERCASE\nconst ENV_KEY_RE_STRICT = /^[A-Z][A-Z0-9_]*$/;\nconst ENV_KEY_RE_LOOSE = /^[A-Za-z_][A-Za-z0-9_]*$/; // Allows any casing\n\nexport function scanProjectForEnvKeys(opts: ScanOptions): ScanResult {\n const root = opts.rootDir;\n const maxCtx = opts.maxContextPerKey ?? 2;\n\n const keyOk = (k: string) =>\n (opts.includeLowercase ? ENV_KEY_RE_LOOSE : ENV_KEY_RE_STRICT).test(k);\n\n const exts = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".cjs\",\n \".json\",\n \".yml\",\n \".yaml\",\n \".toml\",\n ]);\n\n const keys = new Set<string>();\n const contexts: Record<string, KeyContext[]> = {};\n let filesScanned = 0;\n\n walk(root);\n return { keys, filesScanned, contexts };\n\n function addCtx(key: string, relFile: string, line: number, snippet: string) {\n if (!contexts[key]) contexts[key] = [];\n if (contexts[key].length >= maxCtx) return;\n contexts[key].push({\n file: relFile,\n line,\n snippet: snippet.trim().slice(0, 220),\n });\n }\n\n function walk(dir: string) {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!IGNORE_DIRS.has(entry.name)) walk(full);\n continue;\n }\n\n const isEnvFile = entry.name === \".env\" || entry.name.startsWith(\".env.\");\n const ext = path.extname(entry.name);\n\n if (!isEnvFile && !exts.has(ext)) continue;\n\n const content = safeRead(full);\n if (!content) continue;\n\n filesScanned++;\n const rel = path.relative(root, full).replace(/\\\\/g, \"/\");\n\n if (isEnvFile) {\n extractFromEnvFile(content, rel, keys, addCtx, keyOk);\n } else {\n extractFromCode(content, rel, keys, addCtx, keyOk);\n }\n }\n }\n}\n\nfunction extractFromEnvFile(\n text: string,\n relFile: string,\n keys: Set<string>,\n addCtx: (key: string, relFile: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\n // Match any valid identifier in .env files\n const m = ln.match(/^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/);\n if (!m) continue;\n const k = m[1];\n if (!keyOk(k)) continue;\n keys.add(k);\n addCtx(k, relFile, i + 1, ln);\n }\n}\n\nfunction extractFromCode(\n text: string,\n relFile: string,\n keys: Set<string>,\n addCtx: (key: string, relFile: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n const lines = text.split(/\\r?\\n/);\n\n // ONLY explicit env APIs - any valid identifier\n const patterns: RegExp[] = [\n // process.env.KEY or process?.env?.KEY\n /\\bprocess(?:\\?\\.)?\\.env(?:\\?\\.)?\\.([A-Za-z_][A-Za-z0-9_]*)\\b/g,\n // process.env[\"KEY\"] or process.env['KEY']\n /\\bprocess(?:\\?\\.)?\\.env\\[\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\]/g,\n // import.meta.env.KEY\n /\\bimport\\.meta\\.env\\.([A-Za-z_][A-Za-z0-9_]*)\\b/g,\n // Deno.env.get(\"KEY\")\n /\\bDeno\\.env\\.get\\(\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\)/g,\n ];\n\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\n\n for (const re of patterns) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = re.exec(ln))) {\n const k = match[1];\n if (!keyOk(k)) continue;\n keys.add(k);\n addCtx(k, relFile, i + 1, ln);\n }\n }\n }\n}\n\nfunction safeRead(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n","export type AIEnvDoc = {\n key: string;\n description: string;\n where_to_get: string;\n example_value: string;\n is_secret: boolean;\n};\n\nexport type AIGenerateOptions = {\n apiKey: string;\n model: string;\n projectHint?: string;\n contexts: Record<string, { file: string; line: number; snippet: string }[]>;\n keys: string[];\n};\n\nconst JSON_SCHEMA = {\n name: \"env_docs\",\n strict: true,\n schema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n key: { type: \"string\" },\n description: { type: \"string\" },\n where_to_get: { type: \"string\" },\n example_value: { type: \"string\" },\n is_secret: { type: \"boolean\" },\n },\n required: [\n \"key\",\n \"description\",\n \"where_to_get\",\n \"example_value\",\n \"is_secret\",\n ],\n },\n },\n },\n required: [\"items\"],\n },\n} as const;\n\nfunction buildInput(opts: AIGenerateOptions) {\n const lines = opts.keys.map((k) => {\n const ctx = opts.contexts[k]?.[0];\n const seenAt = ctx ? `${ctx.file}:${ctx.line}` : \"unknown\";\n const snippet = ctx ? ctx.snippet : \"\";\n return `- ${k}\\n seen_at: ${seenAt}\\n snippet: ${snippet}`;\n });\n\n const system = [\n \"You generate documentation for environment variables.\",\n \"Return ONLY JSON that matches the provided JSON Schema.\",\n \"Do not include markdown or extra text.\",\n \"Never output real secrets. Use safe placeholders.\",\n \"Keep descriptions short and practical.\",\n \"where_to_get must be actionable (dashboard, secret manager, CI, local service, etc.).\",\n ].join(\" \");\n\n const user = [\n opts.projectHint ? `Project hint: ${opts.projectHint}` : \"\",\n \"Variables:\",\n ...lines,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n return [\n { role: \"system\", content: system },\n { role: \"user\", content: user },\n ];\n}\n\nfunction extractTextFromResponses(data: any): string {\n if (typeof data?.output_text === \"string\" && data.output_text.trim())\n return data.output_text;\n\n const out = data?.output;\n if (Array.isArray(out)) {\n for (const item of out) {\n const content = item?.content;\n if (!Array.isArray(content)) continue;\n for (const c of content) {\n if (typeof c?.text === \"string\" && c.text.trim()) return c.text;\n }\n }\n }\n return \"\";\n}\n\nfunction tryParseJsonLoose(raw: string): any | null {\n // First try direct parse\n try {\n return JSON.parse(raw);\n } catch {\n // Try to extract the first {...} block\n const m = raw.match(/\\{[\\s\\S]*\\}/);\n if (!m) return null;\n try {\n return JSON.parse(m[0]);\n } catch {\n return null;\n }\n }\n}\n\nexport async function generateEnvDocsWithOpenAI(\n opts: AIGenerateOptions\n): Promise<AIEnvDoc[]> {\n const input = buildInput(opts);\n\n const res = await fetch(\"https://api.openai.com/v1/responses\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${opts.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model: opts.model,\n input,\n text: {\n format: {\n type: \"json_schema\",\n ...JSON_SCHEMA,\n },\n },\n }),\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`OpenAI request failed (${res.status}): ${text}`);\n }\n\n const data: any = await res.json();\n const raw = extractTextFromResponses(data).trim();\n\n const parsed = tryParseJsonLoose(raw);\n if (!parsed) {\n throw new Error(\n \"AI output was not valid JSON. Try again, or use a different model.\"\n );\n }\n\n const items = Array.isArray(parsed?.items) ? parsed.items : [];\n return items\n .map((x: any) => ({\n key: String(x.key ?? \"\"),\n description: String(x.description ?? \"\"),\n where_to_get: String(x.where_to_get ?? \"\"),\n example_value: String(x.example_value ?? \"\"),\n is_secret: Boolean(x.is_secret),\n }))\n .filter((x: AIEnvDoc) => x.key.length > 0);\n}\n"],"mappings":"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","names":["fs","path","path","fs"]}
1
+ {"version":3,"sources":["../src/asyq.ts","../src/scan.ts","../src/ai.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\n\nimport { scanProjectForEnvKeys } from \"./scan.js\";\nimport { generateEnvDocsWithOpenAI } from \"./ai.js\";\n\nconst MODELS = [\n \"gpt-5\",\n \"gpt-5-mini\",\n \"gpt-5-nano\",\n \"gpt-4.1\",\n \"gpt-4.1-mini\",\n \"gpt-4.1-nano\",\n] as const;\n\ntype ModelName = (typeof MODELS)[number];\n\nfunction getPackageVersion(): string {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const pkgPath = path.resolve(__dirname, \"../package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n return pkg.version ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction fail(message: string): never {\n p.outro(pc.red(message));\n process.exit(1);\n}\n\nasync function pickMode(): Promise<\"default\" | \"ai\"> {\n const mode = await p.select({\n message: \"How would you like to generate .env.example?\",\n options: [\n { label: \"Default (fast)\", value: \"default\" },\n { label: \"AI-assisted (adds descriptions)\", value: \"ai\" },\n ],\n });\n\n if (p.isCancel(mode)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return mode as \"default\" | \"ai\";\n}\n\nasync function pickModel(): Promise<ModelName> {\n const model = await p.select({\n message: \"Select an AI model\",\n initialValue: \"gpt-4.1-mini\",\n options: MODELS.map((m) => ({ label: m, value: m })),\n });\n\n if (p.isCancel(model)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return model as ModelName;\n}\n\nasync function getApiKey(): Promise<string> {\n const envKey = process.env.OPENAI_API_KEY?.trim();\n if (envKey) return envKey;\n\n const key = await p.password({\n message: \"Enter OpenAI API key (not saved)\",\n validate: (v) =>\n v.trim().length > 0 ? undefined : \"API key cannot be empty\",\n });\n\n if (p.isCancel(key)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return key.trim();\n}\n\n/* ----------------------------- Monorepo support ---------------------------- */\n\nfunction readWorkspaceGlobs(rootAbs: string): string[] {\n const globs: string[] = [];\n\n // pnpm-workspace.yaml\n const pnpmWs = path.join(rootAbs, \"pnpm-workspace.yaml\");\n if (fs.existsSync(pnpmWs)) {\n const txt = fs.readFileSync(pnpmWs, \"utf8\");\n for (const line of txt.split(/\\r?\\n/)) {\n const m = line.match(/^\\s*-\\s*[\"']?([^\"']+)[\"']?\\s*$/);\n if (m) globs.push(m[1].trim());\n }\n }\n\n // package.json workspaces\n const pkgPath = path.join(rootAbs, \"package.json\");\n if (fs.existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n const ws = pkg?.workspaces;\n if (Array.isArray(ws)) globs.push(...ws);\n if (ws && Array.isArray(ws.packages)) globs.push(...ws.packages);\n } catch {\n // ignore\n }\n }\n\n return [...new Set(globs)].filter(Boolean);\n}\n\nfunction expandSimpleGlob(rootAbs: string, pattern: string): string[] {\n const norm = pattern.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n\n if (!norm.includes(\"*\")) {\n const abs = path.join(rootAbs, norm);\n return fs.existsSync(abs) && fs.statSync(abs).isDirectory() ? [norm] : [];\n }\n\n const m = norm.match(/^([^*]+)\\/\\*$/);\n if (!m) return [];\n\n const baseRel = m[1].replace(/\\/+$/, \"\");\n const baseAbs = path.join(rootAbs, baseRel);\n if (!fs.existsSync(baseAbs) || !fs.statSync(baseAbs).isDirectory()) return [];\n\n const out: string[] = [];\n for (const name of fs.readdirSync(baseAbs)) {\n const rel = `${baseRel}/${name}`;\n const abs = path.join(rootAbs, rel);\n if (!fs.statSync(abs).isDirectory()) continue;\n if (fs.existsSync(path.join(abs, \"package.json\"))) out.push(rel);\n }\n return out;\n}\n\nfunction detectWorkspaces(rootAbs: string): string[] {\n const globs = readWorkspaceGlobs(rootAbs);\n const found = new Set<string>();\n\n for (const g of globs) {\n for (const rel of expandSimpleGlob(rootAbs, g)) found.add(rel);\n }\n\n // Turbo-style fallback if no globs detected\n if (found.size === 0) {\n for (const base of [\"apps\", \"packages\"]) {\n const baseAbs = path.join(rootAbs, base);\n if (!fs.existsSync(baseAbs) || !fs.statSync(baseAbs).isDirectory())\n continue;\n for (const name of fs.readdirSync(baseAbs)) {\n const rel = `${base}/${name}`;\n const abs = path.join(rootAbs, rel);\n if (!fs.statSync(abs).isDirectory()) continue;\n if (fs.existsSync(path.join(abs, \"package.json\"))) found.add(rel);\n }\n }\n }\n\n return [...found].sort((a, b) => a.localeCompare(b));\n}\n\n/* -------------------------------------------------------------------------- */\n\nconst program = new Command();\n\nprogram\n .name(\"asyq\")\n .description(\"Generate .env.example by scanning your project for env usage\")\n .version(`v${getPackageVersion()}`);\n\nprogram\n .command(\"init\")\n .description(\"Scan project and generate .env.example\")\n .option(\"--root <dir>\", \"Project root to scan\", \".\")\n .option(\"--out <file>\", \"Output file name\", \".env.example\")\n .option(\"--force\", \"Overwrite output if it exists\")\n .option(\n \"--include-lowercase\",\n \"Include lowercase/mixed-case keys (not recommended)\"\n )\n .option(\"--debug\", \"Print scan diagnostics\")\n .option(\"--monorepo\", \"Generate .env.example for root + each workspace\")\n .action(async (opts) => {\n p.intro(pc.cyan(`Asyq v${getPackageVersion()}`));\n\n const rootAbs = path.resolve(process.cwd(), opts.root);\n const outName = String(opts.out || \".env.example\");\n\n const mode = await pickMode();\n const model: ModelName | null = mode === \"ai\" ? await pickModel() : null;\n\n const targets: { label: string; dirAbs: string }[] = [\n { label: \"root\", dirAbs: rootAbs },\n ];\n\n if (opts.monorepo) {\n const workspaces = detectWorkspaces(rootAbs);\n for (const rel of workspaces) {\n targets.push({ label: rel, dirAbs: path.join(rootAbs, rel) });\n }\n }\n\n // API key once for all targets (AI mode)\n let apiKey = \"\";\n if (mode === \"ai\" && model) {\n apiKey = await getApiKey();\n if (!apiKey) {\n fail(\"OpenAI API key is required for AI-assisted mode.\");\n }\n }\n\n const results: Array<{\n target: string;\n outRel: string;\n keys: number;\n files: number;\n }> = [];\n\n for (const t of targets) {\n const outFileAbs = path.join(t.dirAbs, outName);\n const outRelFromRoot =\n path.relative(rootAbs, outFileAbs).replace(/\\\\/g, \"/\") || outName;\n\n if (fs.existsSync(outFileAbs) && !opts.force) {\n fail(\n `Output already exists: ${outRelFromRoot}. Use --force to overwrite.`\n );\n }\n\n const s = p.spinner();\n s.start(`Scanning ${t.label}`);\n\n const res = scanProjectForEnvKeys({\n rootDir: t.dirAbs,\n includeLowercase: !!opts.includeLowercase,\n });\n\n s.stop(\n `Scanned ${t.label} (${res.filesScanned} files, ${res.keys.size} keys)`\n );\n\n if (opts.debug) {\n p.note(\n [\n `dir: ${t.dirAbs}`,\n `files scanned: ${res.filesScanned}`,\n `keys found: ${res.keys.size}`,\n ].join(\"\\n\"),\n `${t.label} diagnostics`\n );\n }\n\n if (res.keys.size === 0) {\n results.push({\n target: t.label,\n outRel: outRelFromRoot,\n keys: 0,\n files: res.filesScanned,\n });\n continue;\n }\n\n const keys = [...res.keys].sort((a, b) => a.localeCompare(b));\n\n let content = keys.map((k) => `${k}=`).join(\"\\n\") + \"\\n\";\n\n if (mode === \"ai\" && model) {\n const aiSpinner = p.spinner();\n aiSpinner.start(`Generating AI docs for ${t.label}`);\n\n try {\n const docs = await generateEnvDocsWithOpenAI({\n apiKey,\n model,\n projectHint:\n \"Write practical guidance for developers setting env vars.\",\n contexts: res.contexts,\n keys,\n });\n\n aiSpinner.stop(`AI docs generated for ${t.label}`);\n\n const byKey = new Map(docs.map((d) => [d.key, d]));\n\n content =\n keys\n .map((k) => {\n const d = byKey.get(k);\n if (!d) return `${k}=\\n`;\n\n const secretNote = d.is_secret\n ? \"Secret value. Do not commit.\"\n : \"Non-secret value (verify before committing).\";\n\n return [\n `# ${d.key}`,\n `# ${d.description}`,\n `# Where to get it: ${d.where_to_get}`,\n `# ${secretNote}`,\n `${d.key}=${d.example_value || \"\"}`,\n \"\",\n ].join(\"\\n\");\n })\n .join(\"\\n\")\n .trimEnd() + \"\\n\";\n } catch (e: any) {\n aiSpinner.stop(`AI generation failed for ${t.label}`);\n fail(e?.message ?? String(e));\n }\n }\n\n fs.writeFileSync(outFileAbs, content, \"utf8\");\n\n results.push({\n target: t.label,\n outRel: outRelFromRoot,\n keys: keys.length,\n files: res.filesScanned,\n });\n }\n\n // Summary table\n const summary = results\n .map(\n (r) =>\n `${pc.cyan(r.target.padEnd(20))} ${pc.green(\n String(r.keys).padStart(3)\n )} keys → ${pc.dim(r.outRel)}`\n )\n .join(\"\\n\");\n\n p.note(summary, \"Generated\");\n p.outro(pc.green(\"✓ All done!\"));\n });\n\nprogram.parse(process.argv);\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport type ScanOptions = {\n rootDir: string;\n includeLowercase?: boolean;\n maxContextPerKey?: number;\n};\n\nexport type KeyContext = { file: string; line: number; snippet: string };\n\nexport type ScanResult = {\n keys: Set<string>;\n filesScanned: number;\n contexts: Record<string, KeyContext[]>;\n};\n\nconst IGNORE_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \"out\",\n \"coverage\",\n \".turbo\",\n \".cache\",\n \".vercel\",\n \".netlify\",\n]);\n\n// ENV VARS MUST BE EXPLICIT + UPPERCASE\nconst ENV_KEY_RE_STRICT = /^[A-Z][A-Z0-9_]*$/;\nconst ENV_KEY_RE_LOOSE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\nexport function scanProjectForEnvKeys(opts: ScanOptions): ScanResult {\n const root = opts.rootDir;\n const maxCtx = opts.maxContextPerKey ?? 2;\n\n const keyOk = (k: string) =>\n (opts.includeLowercase ? ENV_KEY_RE_LOOSE : ENV_KEY_RE_STRICT).test(k);\n\n const exts = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".cjs\",\n \".json\",\n \".yml\",\n \".yaml\",\n \".toml\",\n ]);\n\n const keys = new Set<string>();\n const contexts: Record<string, KeyContext[]> = {};\n let filesScanned = 0;\n const seenInCode = new Set<string>();\n\n walk(root);\n\n // Only keep keys that were found in actual code, not just .env files\n // Normalize to uppercase for comparison\n const finalKeys = new Set<string>();\n const seenInCodeUpper = new Set([...seenInCode].map((k) => k.toUpperCase()));\n\n for (const key of keys) {\n if (seenInCodeUpper.has(key.toUpperCase())) {\n finalKeys.add(key);\n }\n }\n\n // Add any code-only keys that weren't in .env files\n for (const codeKey of seenInCode) {\n const upper = codeKey.toUpperCase();\n let found = false;\n for (const key of finalKeys) {\n if (key.toUpperCase() === upper) {\n found = true;\n break;\n }\n }\n if (!found && keyOk(codeKey)) {\n finalKeys.add(codeKey);\n }\n }\n\n return { keys: finalKeys, filesScanned, contexts };\n\n function addCtx(key: string, relFile: string, line: number, snippet: string) {\n if (!contexts[key]) contexts[key] = [];\n if (contexts[key].length >= maxCtx) return;\n contexts[key].push({\n file: relFile,\n line,\n snippet: snippet.trim().slice(0, 220),\n });\n }\n\n function walk(dir: string) {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!IGNORE_DIRS.has(entry.name)) walk(full);\n continue;\n }\n\n const isEnvFile = entry.name === \".env\" || entry.name.startsWith(\".env.\");\n const ext = path.extname(entry.name);\n\n if (!isEnvFile && !exts.has(ext)) continue;\n\n const content = safeRead(full);\n if (!content) continue;\n\n filesScanned++;\n const rel = path.relative(root, full).replace(/\\\\/g, \"/\");\n\n if (isEnvFile) {\n extractFromEnvFile(content, rel, keys, addCtx, keyOk);\n } else {\n extractFromCode(content, rel, keys, seenInCode, addCtx, keyOk);\n }\n }\n }\n}\n\nfunction extractFromEnvFile(\n text: string,\n relFile: string,\n keys: Set<string>,\n addCtx: (key: string, relFile: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\n\n // Skip comments and empty lines\n if (!ln.trim() || ln.trim().startsWith(\"#\")) continue;\n\n // Match variable assignments\n const m = ln.match(/^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/);\n if (!m) continue;\n\n const k = m[1];\n if (!keyOk(k)) continue;\n\n keys.add(k);\n addCtx(k, relFile, i + 1, ln);\n }\n}\n\nfunction extractFromCode(\n text: string,\n relFile: string,\n keys: Set<string>,\n seenInCode: Set<string>,\n addCtx: (key: string, relFile: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n const lines = text.split(/\\r?\\n/);\n\n // JavaScript/TypeScript environment variable patterns\n const patterns: RegExp[] = [\n // process.env.KEY or process?.env?.KEY\n /\\bprocess(?:\\?\\.|\\.)env(?:\\?\\.|\\.)([A-Za-z_][A-Za-z0-9_]*)\\b/gi,\n // process.env[\"KEY\"] or process?.env?.[\"KEY\"]\n /\\bprocess(?:\\?\\.|\\.)env\\[\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\]/gi,\n // import.meta.env.KEY (Vite, etc.)\n /\\bimport\\.meta\\.env\\.([A-Za-z_][A-Za-z0-9_]*)\\b/gi,\n // Deno.env.get(\"KEY\")\n /\\bDeno\\.env\\.get\\(\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\)/gi,\n // Bun.env.KEY\n /\\bBun\\.env\\.([A-Za-z_][A-Za-z0-9_]*)\\b/gi,\n ];\n\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\n\n // Skip comments\n if (ln.trim().startsWith(\"//\") || ln.trim().startsWith(\"#\")) continue;\n\n for (const re of patterns) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = re.exec(ln))) {\n const k = match[1];\n if (!keyOk(k)) continue;\n keys.add(k);\n seenInCode.add(k);\n addCtx(k, relFile, i + 1, ln);\n }\n }\n }\n}\n\nfunction safeRead(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n","export type AIEnvDoc = {\n key: string;\n description: string;\n where_to_get: string;\n example_value: string;\n is_secret: boolean;\n};\n\nexport type AIGenerateOptions = {\n apiKey: string;\n model: string;\n projectHint?: string;\n contexts: Record<string, { file: string; line: number; snippet: string }[]>;\n keys: string[];\n};\n\nconst JSON_SCHEMA = {\n name: \"env_docs\",\n strict: true,\n schema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n key: { type: \"string\" },\n description: { type: \"string\" },\n where_to_get: { type: \"string\" },\n example_value: { type: \"string\" },\n is_secret: { type: \"boolean\" },\n },\n required: [\n \"key\",\n \"description\",\n \"where_to_get\",\n \"example_value\",\n \"is_secret\",\n ],\n },\n },\n },\n required: [\"items\"],\n },\n} as const;\n\nfunction buildInput(opts: AIGenerateOptions) {\n const lines = opts.keys.map((k) => {\n const ctx = opts.contexts[k]?.[0];\n const seenAt = ctx ? `${ctx.file}:${ctx.line}` : \"unknown\";\n const snippet = ctx ? ctx.snippet : \"\";\n return `- ${k}\\n seen_at: ${seenAt}\\n snippet: ${snippet}`;\n });\n\n const system = [\n \"You generate documentation for environment variables.\",\n \"Return ONLY JSON that matches the provided JSON Schema.\",\n \"Do not include markdown or extra text.\",\n \"Never output real secrets. Use safe placeholders.\",\n \"Keep descriptions short and practical.\",\n \"where_to_get must be actionable (dashboard, secret manager, CI, local service, etc.).\",\n ].join(\" \");\n\n const user = [\n opts.projectHint ? `Project hint: ${opts.projectHint}` : \"\",\n \"Variables:\",\n ...lines,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n return [\n { role: \"system\", content: system },\n { role: \"user\", content: user },\n ];\n}\n\nfunction extractTextFromResponses(data: any): string {\n if (typeof data?.output_text === \"string\" && data.output_text.trim())\n return data.output_text;\n\n const out = data?.output;\n if (Array.isArray(out)) {\n for (const item of out) {\n const content = item?.content;\n if (!Array.isArray(content)) continue;\n for (const c of content) {\n if (typeof c?.text === \"string\" && c.text.trim()) return c.text;\n }\n }\n }\n return \"\";\n}\n\nfunction tryParseJsonLoose(raw: string): any | null {\n // First try direct parse\n try {\n return JSON.parse(raw);\n } catch {\n // Try to extract the first {...} block\n const m = raw.match(/\\{[\\s\\S]*\\}/);\n if (!m) return null;\n try {\n return JSON.parse(m[0]);\n } catch {\n return null;\n }\n }\n}\n\nexport async function generateEnvDocsWithOpenAI(\n opts: AIGenerateOptions\n): Promise<AIEnvDoc[]> {\n const input = buildInput(opts);\n\n const res = await fetch(\"https://api.openai.com/v1/responses\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${opts.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model: opts.model,\n input,\n text: {\n format: {\n type: \"json_schema\",\n ...JSON_SCHEMA,\n },\n },\n }),\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`OpenAI request failed (${res.status}): ${text}`);\n }\n\n const data: any = await res.json();\n const raw = extractTextFromResponses(data).trim();\n\n const parsed = tryParseJsonLoose(raw);\n if (!parsed) {\n throw new Error(\n \"AI output was not valid JSON. Try again, or use a different model.\"\n );\n }\n\n const items = Array.isArray(parsed?.items) ? parsed.items : [];\n return items\n .map((x: any) => ({\n key: String(x.key ?? \"\"),\n description: String(x.description ?? \"\"),\n where_to_get: String(x.where_to_get ?? \"\"),\n example_value: String(x.example_value ?? \"\"),\n is_secret: Boolean(x.is_secret),\n }))\n .filter((x: AIEnvDoc) => x.key.length > 0);\n}\n"],"mappings":"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","names":["fs","path","path","fs"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asyq",
3
- "version": "8.0.12",
3
+ "version": "8.0.15",
4
4
  "type": "module",
5
5
  "keywords": [
6
6
  "env",
@@ -43,14 +43,13 @@
43
43
  },
44
44
  "scripts": {
45
45
  "build": "tsup",
46
- "pub": "pnpm publish --access=public --no-git-checks",
47
46
  "bump:patch": "pnpm version patch",
48
47
  "bump:minor": "pnpm version minor",
49
48
  "bump:major": "pnpm version major",
50
49
  "commit": "node scripts/commit.js",
51
50
  "push": "node scripts/push.js",
52
- "release:patch": "pnpm commit && pnpm bump:patch && pnpm push && pnpm pub",
53
- "release:minor": "pnpm commit && pnpm bump:minor && pnpm push && pnpm pub",
54
- "release:major": "pnpm commit && pnpm bump:major && pnpm push && pnpm pub"
51
+ "release:patch": "pnpm commit && pnpm bump:patch && pnpm push",
52
+ "release:minor": "pnpm commit && pnpm bump:minor && pnpm push",
53
+ "release:major": "pnpm commit && pnpm bump:major && pnpm push"
55
54
  }
56
55
  }