asyq 8.0.1 → 8.0.3

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
@@ -9,7 +9,8 @@ import ora from "ora";
9
9
  import boxen from "boxen";
10
10
  import logUpdate from "log-update";
11
11
  import TablePkg from "cli-table3";
12
- import { select, password } from "@inquirer/prompts";
12
+ import { select, input } from "@inquirer/prompts";
13
+ import { fileURLToPath } from "url";
13
14
 
14
15
  // src/scan.ts
15
16
  import fs from "fs";
@@ -48,11 +49,11 @@ function scanProjectForEnvKeys(opts) {
48
49
  let filesScanned = 0;
49
50
  walk(root);
50
51
  return { keys, filesScanned, contexts };
51
- function addCtx(key, file, line, snippet) {
52
+ function addCtx(key, relFile, line, snippet) {
52
53
  if (!contexts[key]) contexts[key] = [];
53
54
  if (contexts[key].length >= maxCtx) return;
54
55
  contexts[key].push({
55
- file,
56
+ file: relFile,
56
57
  line,
57
58
  snippet: snippet.trim().slice(0, 220)
58
59
  });
@@ -76,15 +77,16 @@ function scanProjectForEnvKeys(opts) {
76
77
  const content = safeRead(full);
77
78
  if (!content) continue;
78
79
  filesScanned++;
80
+ const rel = path.relative(root, full).replace(/\\/g, "/");
79
81
  if (isEnvFile) {
80
- extractFromEnvFile(content, entry.name, keys, addCtx, keyOk);
82
+ extractFromEnvFile(content, rel, keys, addCtx, keyOk);
81
83
  } else {
82
- extractFromCodeAndConfigs(content, entry.name, keys, addCtx, keyOk);
84
+ extractFromCodeAndConfigs(content, rel, keys, addCtx, keyOk);
83
85
  }
84
86
  }
85
87
  }
86
88
  }
87
- function extractFromEnvFile(text, fileName, keys, addCtx, keyOk) {
89
+ function extractFromEnvFile(text, relFile, keys, addCtx, keyOk) {
88
90
  const lines = text.split(/\r?\n/);
89
91
  for (let i = 0; i < lines.length; i++) {
90
92
  const ln = lines[i];
@@ -93,10 +95,10 @@ function extractFromEnvFile(text, fileName, keys, addCtx, keyOk) {
93
95
  const k = m[1];
94
96
  if (!keyOk(k)) continue;
95
97
  keys.add(k);
96
- addCtx(k, fileName, i + 1, ln);
98
+ addCtx(k, relFile, i + 1, ln);
97
99
  }
98
100
  }
99
- function extractFromCodeAndConfigs(text, fileName, keys, addCtx, keyOk) {
101
+ function extractFromCodeAndConfigs(text, relFile, keys, addCtx, keyOk) {
100
102
  const lines = text.split(/\r?\n/);
101
103
  const patterns = [
102
104
  /\bprocess(?:\?\.)?\.env(?:\?\.)?\.([A-Za-z_][A-Za-z0-9_]*)\b/g,
@@ -114,7 +116,7 @@ function extractFromCodeAndConfigs(text, fileName, keys, addCtx, keyOk) {
114
116
  const k = match[1];
115
117
  if (!keyOk(k)) continue;
116
118
  keys.add(k);
117
- addCtx(k, fileName, i + 1, ln);
119
+ addCtx(k, relFile, i + 1, ln);
118
120
  }
119
121
  }
120
122
  }
@@ -172,6 +174,7 @@ function buildInput(opts) {
172
174
  const system = [
173
175
  "You generate documentation for environment variables.",
174
176
  "Return ONLY JSON that matches the provided JSON Schema.",
177
+ "Do not include markdown or extra text.",
175
178
  "Never output real secrets. Use safe placeholders.",
176
179
  "Keep descriptions short and practical.",
177
180
  "where_to_get must be actionable (dashboard, secret manager, CI, local service, etc.)."
@@ -201,8 +204,21 @@ function extractTextFromResponses(data) {
201
204
  }
202
205
  return "";
203
206
  }
207
+ function tryParseJsonLoose(raw) {
208
+ try {
209
+ return JSON.parse(raw);
210
+ } catch {
211
+ const m = raw.match(/\{[\s\S]*\}/);
212
+ if (!m) return null;
213
+ try {
214
+ return JSON.parse(m[0]);
215
+ } catch {
216
+ return null;
217
+ }
218
+ }
219
+ }
204
220
  async function generateEnvDocsWithOpenAI(opts) {
205
- const input = buildInput(opts);
221
+ const input2 = buildInput(opts);
206
222
  const res = await fetch("https://api.openai.com/v1/responses", {
207
223
  method: "POST",
208
224
  headers: {
@@ -211,7 +227,7 @@ async function generateEnvDocsWithOpenAI(opts) {
211
227
  },
212
228
  body: JSON.stringify({
213
229
  model: opts.model,
214
- input,
230
+ input: input2,
215
231
  text: {
216
232
  format: {
217
233
  type: "json_schema",
@@ -226,12 +242,10 @@ async function generateEnvDocsWithOpenAI(opts) {
226
242
  }
227
243
  const data = await res.json();
228
244
  const raw = extractTextFromResponses(data).trim();
229
- let parsed;
230
- try {
231
- parsed = JSON.parse(raw);
232
- } catch {
245
+ const parsed = tryParseJsonLoose(raw);
246
+ if (!parsed) {
233
247
  throw new Error(
234
- "AI output was not valid JSON (structured output expected)."
248
+ "AI output was not valid JSON. Try again, or use a different model."
235
249
  );
236
250
  }
237
251
  const items = Array.isArray(parsed?.items) ? parsed.items : [];
@@ -245,7 +259,15 @@ async function generateEnvDocsWithOpenAI(opts) {
245
259
  }
246
260
 
247
261
  // src/asyq.ts
248
- import { fileURLToPath } from "url";
262
+ var Table = TablePkg.default ?? TablePkg;
263
+ var MODELS = [
264
+ "gpt-5",
265
+ "gpt-5-mini",
266
+ "gpt-5-nano",
267
+ "gpt-4.1",
268
+ "gpt-4.1-mini",
269
+ "gpt-4.1-nano"
270
+ ];
249
271
  function getPackageVersion() {
250
272
  try {
251
273
  const __filename = fileURLToPath(import.meta.url);
@@ -257,19 +279,9 @@ function getPackageVersion() {
257
279
  return "unknown";
258
280
  }
259
281
  }
260
- var Table = TablePkg.default ?? TablePkg;
261
- var MODELS = [
262
- "gpt-5",
263
- "gpt-5-mini",
264
- "gpt-5-nano",
265
- "gpt-4.1",
266
- "gpt-4.1-mini",
267
- "gpt-4.1-nano"
268
- ];
269
282
  function renderHeader() {
270
283
  const body = [
271
284
  pc.bold(`Asyq v${getPackageVersion()}`),
272
- pc.dim(""),
273
285
  pc.dim("Generate .env.example from your project\u2019s env usage"),
274
286
  pc.dim("Created by @thev1ndu")
275
287
  ].join("\n");
@@ -324,14 +336,14 @@ async function pickModel() {
324
336
  async function getApiKey() {
325
337
  const envKey = process.env.OPENAI_API_KEY?.trim();
326
338
  if (envKey) return envKey;
327
- const key = await password({
339
+ const key = await input({
328
340
  message: "Enter OpenAI API key (not saved)",
329
- mask: "*"
341
+ validate: (v) => v.trim().length > 0 || "API key cannot be empty"
330
342
  });
331
- return String(key ?? "").trim();
343
+ return key.trim();
332
344
  }
333
345
  var program = new Command();
334
- program.name("Asyq").description("Generate .env.example by scanning your project for env usage").version(`v${getPackageVersion()}`);
346
+ program.name("asyq").description("Generate .env.example by scanning your project for env usage").version(`v${getPackageVersion()}`);
335
347
  program.command("init").description("Scan project and generate .env.example").option("--root <dir>", "Project root to scan", ".").option("--out <file>", "Output file", ".env.example").option("--force", "Overwrite output if it exists").option(
336
348
  "--include-lowercase",
337
349
  "Include lowercase/mixed-case keys (not recommended)"
@@ -391,15 +403,6 @@ program.command("init").description("Scan project and generate .env.example").op
391
403
  let content = keys.map((k) => `${k}=`).join("\n") + "\n";
392
404
  if (mode === "ai" && model) {
393
405
  const apiKey = await getApiKey();
394
- if (!apiKey) {
395
- steps[2].status = "fail";
396
- renderSteps(steps);
397
- finishSteps();
398
- fail(
399
- "OpenAI API key is required for AI-assisted mode.",
400
- "Set OPENAI_API_KEY or enter it when prompted."
401
- );
402
- }
403
406
  const aiSpinner = ora({
404
407
  text: "Generating AI guidance",
405
408
  spinner: "dots"
@@ -416,15 +419,8 @@ program.command("init").description("Scan project and generate .env.example").op
416
419
  const byKey = new Map(docs.map((d) => [d.key, d]));
417
420
  content = keys.map((k) => {
418
421
  const d = byKey.get(k);
419
- if (!d) {
420
- return [
421
- `# ${k}`,
422
- `# Description: not provided`,
423
- `# Where to get it: not provided`,
424
- `${k}=`,
425
- ""
426
- ].join("\n");
427
- }
422
+ if (!d) return `${k}=
423
+ `;
428
424
  const secretNote = d.is_secret ? "Secret value. Do not commit." : "Non-secret value (verify before committing).";
429
425
  return [
430
426
  `# ${d.key}`,
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 pc from \"picocolors\";\nimport ora from \"ora\";\nimport boxen from \"boxen\";\nimport logUpdate from \"log-update\";\nimport TablePkg from \"cli-table3\";\nimport { select, password } from \"@inquirer/prompts\";\n\nimport { scanProjectForEnvKeys } from \"./scan.js\";\nimport { generateEnvDocsWithOpenAI } from \"./ai.js\";\n\nimport { fileURLToPath } from \"node:url\";\n\nfunction getPackageVersion(): string {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n\n const pkgPath = path.resolve(__dirname, \"../package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n\n return pkg.version ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\n// cli-table3 interop safety (works in ESM + CJS environments)\nconst Table: any = (TablePkg as any).default ?? (TablePkg as any);\n\ntype Step = {\n title: string;\n status: \"pending\" | \"running\" | \"done\" | \"fail\";\n detail?: string;\n};\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 renderHeader() {\n const body = [\n pc.bold(`Asyq v${getPackageVersion()}`),\n pc.dim(\"\"),\n pc.dim(\"Generate .env.example from your project’s env usage\"),\n pc.dim(\"Created by @thev1ndu\"),\n ].join(\"\\n\");\n\n console.log(\n boxen(body, {\n padding: 1,\n borderStyle: \"round\",\n borderColor: \"cyan\",\n })\n );\n console.log(\"\");\n}\n\nfunction icon(status: Step[\"status\"]) {\n if (status === \"done\") return pc.green(\"✓\");\n if (status === \"fail\") return pc.red(\"✗\");\n if (status === \"running\") return pc.cyan(\"•\");\n return pc.dim(\"•\");\n}\n\nfunction renderSteps(steps: Step[]) {\n logUpdate(\n steps\n .map((s) => {\n const left = `${icon(s.status)} ${s.title}`;\n const right = s.detail ? pc.dim(s.detail) : \"\";\n return right ? `${left} ${right}` : left;\n })\n .join(\"\\n\")\n );\n}\n\nfunction finishSteps() {\n logUpdate.done();\n}\n\nfunction fail(message: string, hint?: string): never {\n console.error(pc.red(message));\n if (hint) console.error(pc.dim(hint));\n process.exit(1);\n}\n\nasync function pickMode(): Promise<\"default\" | \"ai\"> {\n return await select({\n message: \"How would you like to generate .env.example?\",\n choices: [\n { name: \"Default\", value: \"default\" },\n { name: \"AI-assisted\", value: \"ai\" },\n ],\n });\n}\n\nasync function pickModel(): Promise<ModelName> {\n return await select({\n message: \"Select an AI model\",\n default: \"gpt-4.1-mini\",\n choices: MODELS.map((m) => ({ name: m, value: m })),\n });\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 password({\n message: \"Enter OpenAI API key (not saved)\",\n mask: \"*\",\n });\n\n return String(key ?? \"\").trim();\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\", \".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 .action(async (opts) => {\n renderHeader();\n\n const root = path.resolve(process.cwd(), opts.root);\n const outFile = path.resolve(process.cwd(), opts.out);\n\n if (fs.existsSync(outFile) && !opts.force) {\n fail(`Output already exists: ${opts.out}`, \"Use --force to overwrite.\");\n }\n\n const mode = await pickMode();\n const model: ModelName | null = mode === \"ai\" ? await pickModel() : null;\n\n const steps: Step[] = [\n { title: \"Preparing\", status: \"running\", detail: `root: ${opts.root}` },\n { title: \"Scanning project files\", status: \"pending\" },\n {\n title: \"Writing .env.example\",\n status: \"pending\",\n detail: mode === \"ai\" ? `AI (${model})` : \"Default\",\n },\n ];\n\n renderSteps(steps);\n\n steps[0].status = \"done\";\n steps[1].status = \"running\";\n renderSteps(steps);\n\n const scanSpinner = ora({\n text: \"Scanning for env keys\",\n spinner: \"dots\",\n }).start();\n\n const res = scanProjectForEnvKeys({\n rootDir: root,\n includeLowercase: !!opts.includeLowercase,\n });\n\n scanSpinner.stop();\n\n steps[1].status = \"done\";\n steps[1].detail = `${res.filesScanned} files scanned`;\n steps[2].status = \"running\";\n renderSteps(steps);\n\n if (opts.debug) {\n console.log(pc.dim(\"\"));\n console.log(pc.dim(\"Diagnostics\"));\n console.log(pc.dim(` root: ${opts.root}`));\n console.log(pc.dim(` files scanned: ${res.filesScanned}`));\n console.log(pc.dim(` keys found: ${res.keys.size}`));\n console.log(pc.dim(\"\"));\n }\n\n if (res.keys.size === 0) {\n steps[2].status = \"fail\";\n renderSteps(steps);\n finishSteps();\n fail(\n \"No environment variables found.\",\n \"Ensure your code uses process.env.KEY or ${KEY}.\"\n );\n }\n\n const keys = [...res.keys].sort((a, b) => a.localeCompare(b));\n\n // Default content\n let content = keys.map((k) => `${k}=`).join(\"\\n\") + \"\\n\";\n\n if (mode === \"ai\" && model) {\n const apiKey = await getApiKey();\n if (!apiKey) {\n steps[2].status = \"fail\";\n renderSteps(steps);\n finishSteps();\n fail(\n \"OpenAI API key is required for AI-assisted mode.\",\n \"Set OPENAI_API_KEY or enter it when prompted.\"\n );\n }\n\n const aiSpinner = ora({\n text: \"Generating AI guidance\",\n spinner: \"dots\",\n }).start();\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();\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) {\n return [\n `# ${k}`,\n `# Description: not provided`,\n `# Where to get it: not provided`,\n `${k}=`,\n \"\",\n ].join(\"\\n\");\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();\n steps[2].status = \"fail\";\n renderSteps(steps);\n finishSteps();\n fail(\"AI generation failed.\", e?.message ?? String(e));\n }\n }\n\n fs.writeFileSync(outFile, content, \"utf8\");\n\n steps[2].status = \"done\";\n renderSteps(steps);\n finishSteps();\n\n const table = new Table({\n style: { head: [], border: [] },\n colWidths: [18, 60],\n wordWrap: true,\n });\n\n table.push(\n [pc.dim(\"Output\"), pc.cyan(opts.out)],\n [pc.dim(\"Keys\"), pc.cyan(String(keys.length))],\n [pc.dim(\"Mode\"), pc.cyan(mode === \"ai\" ? `AI (${model})` : \"Default\")]\n );\n\n console.log(\"\");\n console.log(pc.bold(\"Complete\"));\n console.log(table.toString());\n console.log(pc.dim(\"Next steps\"));\n console.log(pc.dim(` 1) Fill values in ${opts.out}`));\n console.log(pc.dim(\" 2) Copy to .env (do not commit secrets)\"));\n console.log(\"\");\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// Enterprise-safe default: UPPERCASE env keys only\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 // Scan common source/config formats (avoid HTML/MD noise)\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\n return { keys, filesScanned, contexts };\n\n function addCtx(key: string, file: string, line: number, snippet: string) {\n if (!contexts[key]) contexts[key] = [];\n if (contexts[key].length >= maxCtx) return;\n contexts[key].push({\n file,\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\n if (isEnvFile) {\n extractFromEnvFile(content, entry.name, keys, addCtx, keyOk);\n } else {\n extractFromCodeAndConfigs(content, entry.name, keys, addCtx, keyOk);\n }\n }\n }\n}\n\nfunction extractFromEnvFile(\n text: string,\n fileName: string,\n keys: Set<string>,\n addCtx: (key: string, file: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n // KEY=... or export KEY=...\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\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, fileName, i + 1, ln);\n }\n}\n\nfunction extractFromCodeAndConfigs(\n text: string,\n fileName: string,\n keys: Set<string>,\n addCtx: (key: string, file: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n const lines = text.split(/\\r?\\n/);\n\n // Only structured env patterns here (NO generic KEY= parsing)\n const patterns: RegExp[] = [\n /\\bprocess(?:\\?\\.)?\\.env(?:\\?\\.)?\\.([A-Za-z_][A-Za-z0-9_]*)\\b/g,\n /\\bprocess(?:\\?\\.)?\\.env\\[\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\]/g,\n /\\bimport\\.meta\\.env\\.([A-Za-z_][A-Za-z0-9_]*)\\b/g,\n /\\bDeno\\.env\\.get\\(\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\)/g,\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/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 while ((match = re.exec(ln))) {\n const k = match[1];\n if (!keyOk(k)) continue;\n keys.add(k);\n addCtx(k, fileName, 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 \"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 // Try to find text content in output array\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\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 let parsed: any;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(\n \"AI output was not valid JSON (structured output expected).\"\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 pc from \"picocolors\";\nimport ora from \"ora\";\nimport boxen from \"boxen\";\nimport logUpdate from \"log-update\";\nimport TablePkg from \"cli-table3\";\nimport { select, input } from \"@inquirer/prompts\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { scanProjectForEnvKeys } from \"./scan.js\";\nimport { generateEnvDocsWithOpenAI } from \"./ai.js\";\n\n// cli-table3 interop safety (works in ESM + CJS environments)\nconst Table: any = (TablePkg as any).default ?? (TablePkg as any);\n\ntype Step = {\n title: string;\n status: \"pending\" | \"running\" | \"done\" | \"fail\";\n detail?: string;\n};\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 renderHeader() {\n const body = [\n pc.bold(`Asyq v${getPackageVersion()}`),\n pc.dim(\"Generate .env.example from your project’s env usage\"),\n pc.dim(\"Created by @thev1ndu\"),\n ].join(\"\\n\");\n\n console.log(\n boxen(body, {\n padding: 1,\n borderStyle: \"round\",\n borderColor: \"cyan\",\n })\n );\n console.log(\"\");\n}\n\nfunction icon(status: Step[\"status\"]) {\n if (status === \"done\") return pc.green(\"✓\");\n if (status === \"fail\") return pc.red(\"✗\");\n if (status === \"running\") return pc.cyan(\"•\");\n return pc.dim(\"•\");\n}\n\nfunction renderSteps(steps: Step[]) {\n logUpdate(\n steps\n .map((s) => {\n const left = `${icon(s.status)} ${s.title}`;\n const right = s.detail ? pc.dim(s.detail) : \"\";\n return right ? `${left} ${right}` : left;\n })\n .join(\"\\n\")\n );\n}\n\nfunction finishSteps() {\n logUpdate.done();\n}\n\nfunction fail(message: string, hint?: string): never {\n console.error(pc.red(message));\n if (hint) console.error(pc.dim(hint));\n process.exit(1);\n}\n\nasync function pickMode(): Promise<\"default\" | \"ai\"> {\n return await select({\n message: \"How would you like to generate .env.example?\",\n choices: [\n { name: \"Default\", value: \"default\" },\n { name: \"AI-assisted\", value: \"ai\" },\n ],\n });\n}\n\nasync function pickModel(): Promise<ModelName> {\n return await select({\n message: \"Select an AI model\",\n default: \"gpt-4.1-mini\",\n choices: MODELS.map((m) => ({ name: m, value: m })),\n });\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 input({\n message: \"Enter OpenAI API key (not saved)\",\n validate: (v) => v.trim().length > 0 || \"API key cannot be empty\",\n });\n\n return key.trim();\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\", \".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 .action(async (opts) => {\n renderHeader();\n\n const root = path.resolve(process.cwd(), opts.root);\n const outFile = path.resolve(process.cwd(), opts.out);\n\n if (fs.existsSync(outFile) && !opts.force) {\n fail(`Output already exists: ${opts.out}`, \"Use --force to overwrite.\");\n }\n\n const mode = await pickMode();\n const model: ModelName | null = mode === \"ai\" ? await pickModel() : null;\n\n const steps: Step[] = [\n { title: \"Preparing\", status: \"running\", detail: `root: ${opts.root}` },\n { title: \"Scanning project files\", status: \"pending\" },\n {\n title: \"Writing .env.example\",\n status: \"pending\",\n detail: mode === \"ai\" ? `AI (${model})` : \"Default\",\n },\n ];\n\n renderSteps(steps);\n\n steps[0].status = \"done\";\n steps[1].status = \"running\";\n renderSteps(steps);\n\n const scanSpinner = ora({\n text: \"Scanning for env keys\",\n spinner: \"dots\",\n }).start();\n\n const res = scanProjectForEnvKeys({\n rootDir: root,\n includeLowercase: !!opts.includeLowercase,\n });\n\n scanSpinner.stop();\n\n steps[1].status = \"done\";\n steps[1].detail = `${res.filesScanned} files scanned`;\n steps[2].status = \"running\";\n renderSteps(steps);\n\n if (opts.debug) {\n console.log(pc.dim(\"\"));\n console.log(pc.dim(\"Diagnostics\"));\n console.log(pc.dim(` root: ${opts.root}`));\n console.log(pc.dim(` files scanned: ${res.filesScanned}`));\n console.log(pc.dim(` keys found: ${res.keys.size}`));\n console.log(pc.dim(\"\"));\n }\n\n if (res.keys.size === 0) {\n steps[2].status = \"fail\";\n renderSteps(steps);\n finishSteps();\n fail(\n \"No environment variables found.\",\n \"Ensure your code uses process.env.KEY or ${KEY}.\"\n );\n }\n\n const keys = [...res.keys].sort((a, b) => a.localeCompare(b));\n\n // Default content\n let content = keys.map((k) => `${k}=`).join(\"\\n\") + \"\\n\";\n\n if (mode === \"ai\" && model) {\n const apiKey = await getApiKey();\n\n const aiSpinner = ora({\n text: \"Generating AI guidance\",\n spinner: \"dots\",\n }).start();\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();\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();\n steps[2].status = \"fail\";\n renderSteps(steps);\n finishSteps();\n fail(\"AI generation failed.\", e?.message ?? String(e));\n }\n }\n\n fs.writeFileSync(outFile, content, \"utf8\");\n\n steps[2].status = \"done\";\n renderSteps(steps);\n finishSteps();\n\n const table = new Table({\n style: { head: [], border: [] },\n colWidths: [18, 60],\n wordWrap: true,\n });\n\n table.push(\n [pc.dim(\"Output\"), pc.cyan(opts.out)],\n [pc.dim(\"Keys\"), pc.cyan(String(keys.length))],\n [pc.dim(\"Mode\"), pc.cyan(mode === \"ai\" ? `AI (${model})` : \"Default\")]\n );\n\n console.log(\"\");\n console.log(pc.bold(\"Complete\"));\n console.log(table.toString());\n console.log(pc.dim(\"Next steps\"));\n console.log(pc.dim(` 1) Fill values in ${opts.out}`));\n console.log(pc.dim(\" 2) Copy to .env (do not commit secrets)\"));\n console.log(\"\");\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\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\n walk(root);\n\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\n const rel = path.relative(root, full).replace(/\\\\/g, \"/\");\n\n if (isEnvFile) {\n extractFromEnvFile(content, rel, keys, addCtx, keyOk);\n } else {\n extractFromCodeAndConfigs(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 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 extractFromCodeAndConfigs(\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 const patterns: RegExp[] = [\n /\\bprocess(?:\\?\\.)?\\.env(?:\\?\\.)?\\.([A-Za-z_][A-Za-z0-9_]*)\\b/g,\n /\\bprocess(?:\\?\\.)?\\.env\\[\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\]/g,\n /\\bimport\\.meta\\.env\\.([A-Za-z_][A-Za-z0-9_]*)\\b/g,\n /\\bDeno\\.env\\.get\\(\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\)/g,\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/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 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","input","path","fs"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asyq",
3
- "version": "8.0.1",
3
+ "version": "8.0.3",
4
4
  "type": "module",
5
5
  "keywords": [
6
6
  "env",