prodex 1.2.0 → 1.3.0

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.
@@ -7,76 +7,76 @@ exports.pickEntries = pickEntries;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const inquirer_1 = __importDefault(require("inquirer"));
10
- const config_1 = require("../constants/config");
11
10
  const helpers_1 = require("../core/helpers");
12
11
  const file_utils_1 = require("../core/file-utils");
13
12
  const utils_1 = require("../lib/utils");
14
13
  const logger_1 = require("../lib/logger");
15
- async function pickEntries(baseDirs, depth = 2, cfg) {
16
- const entryPatterns = cfg.entry?.files || [];
17
- const priorities = [...entryPatterns, ...(cfg.entry?.priority || [])];
18
- const verbose = !!cfg.verbose;
19
- // 1) Resolve patterns to absolute files and preselect them
20
- const resolvedEntries = (await (0, file_utils_1.globScan)(entryPatterns, { cwd: config_1.ROOT })).files;
14
+ const prompt_1 = require("../lib/prompt");
15
+ const questions_1 = require("../lib/questions");
16
+ /**
17
+ * Interactive entry picker for Prodex.
18
+ * Handles depth-based scanning, caching, and priority ordering.
19
+ */
20
+ async function pickEntries(cfg) {
21
+ const { root, entry: { files, ui: { roots = [], priority = [], scanDepth }, }, } = cfg;
22
+ let depth = scanDepth;
23
+ const entryPatterns = files || [];
24
+ const priorities = [...entryPatterns, ...priority];
25
+ // 1️⃣ Resolve pre-defined entry patterns
26
+ const resolvedEntries = (await (0, file_utils_1.globScan)(entryPatterns, { cwd: root })).files;
21
27
  let selected = [...resolvedEntries];
22
- // cache: depth -> files[]
28
+ // cache: depth files[]
23
29
  const scanCache = new Map();
24
30
  for (;;) {
25
- const files = await getFilesAtDepth({
26
- baseDirs,
27
- depth,
28
- cfg,
29
- scanCache,
30
- verbose,
31
- });
31
+ const files = await getFilesAtDepth(depth, cfg, scanCache);
32
32
  // Merge resolved entries with current scan results
33
33
  const combined = (0, utils_1.unique)([...resolvedEntries, ...files]);
34
- // Priority-aware ordering: entries first, then other priorities
35
34
  const sorted = (0, helpers_1.orderByPriority)(combined, priorities);
36
35
  // Build UI selection list
37
- const choices = sorted.map(f => ({
38
- name: path_1.default.relative(config_1.ROOT, f).norm(),
36
+ const choices = sorted.map((f) => ({
37
+ name: (0, helpers_1.rel)(f, root),
39
38
  value: f,
40
39
  checked: selected.includes(f),
41
40
  }));
42
- choices.push(new inquirer_1.default.Separator());
43
- choices.push({ name: "🔽 Load more (go deeper)", value: "__loadmore" });
44
- const { picks } = await inquirer_1.default.prompt([
45
- {
46
- type: "checkbox",
47
- name: "picks",
48
- message: `Select entry files (depth ${depth})`,
49
- choices,
50
- loop: false,
51
- pageSize: 20,
52
- }
53
- ]);
41
+ if (depth < scanDepth + 5) {
42
+ choices.push(new inquirer_1.default.Separator());
43
+ choices.push({ name: "🔽 Load more (go deeper)", value: "__loadmore" });
44
+ }
45
+ // 🧠 Use unified prompt wrapper
46
+ const answers = await (0, prompt_1.prompt)((0, questions_1.PICK_ENTRIES_QUESTION)(choices, depth), { picks: [] });
47
+ if (!answers)
48
+ return (0, utils_1.unique)(selected);
49
+ const { picks } = answers;
54
50
  if (picks.includes("__loadmore")) {
55
51
  depth++;
56
- selected = picks.filter(p => p !== "__loadmore");
52
+ selected = picks.filter((p) => p !== "__loadmore");
57
53
  continue;
58
54
  }
59
- selected = picks.filter(p => p !== "__loadmore");
55
+ selected = picks.filter((p) => p !== "__loadmore");
60
56
  break;
61
57
  }
62
58
  return (0, utils_1.unique)(selected);
63
59
  }
64
- async function getFilesAtDepth({ baseDirs, depth, cfg, scanCache, verbose }) {
60
+ /**
61
+ * Depth-based directory scanner with caching.
62
+ */
63
+ async function getFilesAtDepth(depth, cfg, scanCache) {
64
+ const baseDirs = cfg.entry.ui.roots || [];
65
65
  if (scanCache.has(depth)) {
66
- logger_1.logger.debug(`[cache] depth=${depth} ✓`);
66
+ logger_1.logger.debug(`[picker] cache hit → depth=${depth}`);
67
67
  return scanCache.get(depth);
68
68
  }
69
- logger_1.logger.debug(`[scan] depth=${depth} …`);
69
+ logger_1.logger.debug(`[picker] scanning → depth=${depth}`);
70
70
  const files = [];
71
71
  const effectiveCfg = { ...cfg, scanDepth: depth };
72
72
  for (const base of baseDirs) {
73
- const full = path_1.default.join(config_1.ROOT, base);
73
+ const full = path_1.default.join(cfg.root, base);
74
74
  if (!fs_1.default.existsSync(full))
75
75
  continue;
76
76
  for (const f of (0, helpers_1.walk)(full, effectiveCfg, 0))
77
77
  files.push(f.norm());
78
78
  }
79
79
  scanCache.set(depth, files);
80
- logger_1.logger.debug(`[scan] depth=${depth} found=${files.length}`);
80
+ logger_1.logger.debug(`[picker] depth=${depth} found=${files.length}`);
81
81
  return files;
82
82
  }
@@ -1,14 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.showSummary = showSummary;
4
- exports.importSummary = importSummary;
3
+ exports.endSummary = endSummary;
4
+ exports.introSummary = introSummary;
5
+ exports.entrySummary = entrySummary;
5
6
  const logger_1 = require("../lib/logger");
6
- function showSummary({ outDir, fileName, entries }) {
7
- logger_1.logger.debug(`🧩 Active Run → ${fileName} (${entries.length} entries)`);
8
- }
9
- function importSummary(result) {
10
- logger_1.logger.debug(`\n🧩 Summary:
7
+ const helpers_1 = require("../core/helpers");
8
+ function endSummary(out, result) {
9
+ logger_1.logger.debug(`🧩 Summary:
11
10
  • Unique imports expected: ${result.stats.expected.size}
12
11
  • Unique imports resolved: ${result.stats.resolved.size}
13
12
  `);
13
+ logger_1.logger.log(`✅ ${out.norm()}`);
14
+ }
15
+ function introSummary({ flags, config }) {
16
+ logger_1.logger.log(`------- PRODEx RUN @ ${new Date().toLocaleTimeString()} — Codebase decoded -------`);
17
+ // Log parse results for testing
18
+ logger_1.logger.debug("🧩 Parsed CLI input:", _2j({ flags }));
19
+ logger_1.logger.debug("Final merged config:", _2j(config));
20
+ }
21
+ function entrySummary(entries) {
22
+ let result = "📋 You selected:";
23
+ for (const e of entries)
24
+ result += "\n -" + (0, helpers_1.rel)(e);
25
+ logger_1.logger.log(result);
14
26
  }
@@ -8,6 +8,7 @@ const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const default_config_1 = require("./default-config");
10
10
  const logger_1 = require("../lib/logger");
11
+ const utils_1 = require("../lib/utils");
11
12
  /**
12
13
  * 🧩 Load and merge Prodex configuration (v3)
13
14
  *
@@ -28,7 +29,7 @@ async function loadProdexConfig(flags = {}, cwd) {
28
29
  logger_1.logger.info("No prodex.json found — using defaults.");
29
30
  }
30
31
  // 2️⃣ Merge defaults → user config
31
- const { output, entry, resolve, debug } = default_config_1.DEFAULT_PRODEX_CONFIG;
32
+ const { output, entry, resolve } = default_config_1.DEFAULT_PRODEX_CONFIG;
32
33
  const cfg = {
33
34
  ...default_config_1.DEFAULT_PRODEX_CONFIG,
34
35
  ...userConfig,
@@ -39,11 +40,12 @@ async function loadProdexConfig(flags = {}, cwd) {
39
40
  ui: { ...entry.ui, ...userConfig.entry?.ui },
40
41
  },
41
42
  resolve: { ...resolve, ...userConfig.resolve },
42
- debug: { ...debug, ...userConfig.debug },
43
43
  root: cwd,
44
+ name: flags?.name,
44
45
  };
45
46
  // 4️⃣ Apply CLI flag overrides (if any)
46
47
  applyFlagOverrides(cfg, flags);
48
+ tidyArrayFields(cfg);
47
49
  return cfg;
48
50
  }
49
51
  /** Merge CLI flags into config where relevant. */
@@ -52,8 +54,7 @@ function applyFlagOverrides(cfg, flags) {
52
54
  if (!flags)
53
55
  return;
54
56
  const outputOverrides = {
55
- name: (cfg, v) => (cfg.output.prefix = v),
56
- txt: (cfg) => (cfg.output.format = "txt"),
57
+ txt: (cfg, v) => (cfg.output.format = v ? "txt" : "md"),
57
58
  };
58
59
  const resolveOverrides = {
59
60
  limit: (cfg, v) => (cfg.resolve.limit = v),
@@ -84,7 +85,11 @@ function applyFlagOverrides(cfg, flags) {
84
85
  // Conditional override rule:
85
86
  // If files exist and include was null/undefined → clear include array
86
87
  const hasFiles = Array.isArray(flags.files) ? flags.files.length > 0 : !!flags.files;
87
- if (hasFiles && flags.include == null) {
88
+ if (hasFiles && !flags.include) {
88
89
  cfg.resolve.include = [];
89
90
  }
90
91
  }
92
+ function tidyArrayFields(cfg) {
93
+ cfg.entry.files = (0, utils_1.normalizePatterns)(cfg.entry.files);
94
+ ["include", "exclude"].forEach((k) => (cfg.resolve[k] = cfg.resolve[k]));
95
+ }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DTS_EXT = exports.BASE_EXTS = exports.GLOBAL_IGNORE = exports.RESOLVERS = exports.CODE_EXTS = exports.ROOT = void 0;
4
- exports.ROOT = process.cwd();
3
+ exports.VALID_GLOB_CHARS = exports.SUFFIX = exports.DTS_EXT = exports.BASE_EXTS = exports.GLOBAL_IGNORE = exports.RESOLVERS = exports.CODE_EXTS = void 0;
5
4
  exports.CODE_EXTS = [".js", ".mjs", ".ts", ".tsx", ".d.ts", ".php"];
6
5
  const js_resolver_1 = require("../resolvers/js/js-resolver");
7
6
  const php_resolver_1 = require("../resolvers/php/php-resolver");
@@ -10,8 +9,18 @@ exports.RESOLVERS = {
10
9
  ".ts": js_resolver_1.resolveJsImports,
11
10
  ".tsx": js_resolver_1.resolveJsImports,
12
11
  ".d.ts": js_resolver_1.resolveJsImports,
13
- ".js": js_resolver_1.resolveJsImports
12
+ ".js": js_resolver_1.resolveJsImports,
14
13
  };
15
14
  exports.GLOBAL_IGNORE = ["**/node_modules/**", "**/vendor/**", "**/dist/**"];
16
15
  exports.BASE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".types"];
17
16
  exports.DTS_EXT = ".d.ts";
17
+ exports.SUFFIX = "trace";
18
+ /**
19
+ * Normalize and sanitize pattern fields.
20
+ * - Always returns an array.
21
+ * - Accepts string or string[].
22
+ * - Splits comma-separated strings.
23
+ * - Trims and removes empty elements.
24
+ * - Filters out invalid or unusable characters for Fast-Glob.
25
+ */
26
+ exports.VALID_GLOB_CHARS = /^[\w\-@./*?|!{}\[\]^$()+]+$/; // allows Fast-Glob-safe symbols
@@ -9,7 +9,7 @@ exports.DEFAULT_PRODEX_CONFIG = {
9
9
  version: 3.1,
10
10
  output: {
11
11
  dir: "prodex",
12
- versioned: false,
12
+ versioned: true,
13
13
  prefix: "combined",
14
14
  format: "md",
15
15
  },
@@ -18,13 +18,7 @@ exports.DEFAULT_PRODEX_CONFIG = {
18
18
  ui: {
19
19
  roots: ["app", "routes", "resources/js/**"],
20
20
  scanDepth: 2,
21
- priority: [
22
- "**/routes/web.php",
23
- "**/routes/api.php",
24
- "**/*index.*",
25
- "**/*main.*",
26
- "**/app.*"
27
- ],
21
+ priority: ["**/routes/web.php", "**/routes/api.php", "**/*index.*", "**/*main.*", "**/app.*"],
28
22
  },
29
23
  },
30
24
  resolve: {
@@ -33,16 +27,8 @@ exports.DEFAULT_PRODEX_CONFIG = {
33
27
  "@hooks": "resources/js/hooks",
34
28
  "@data": "resources/js/data",
35
29
  },
36
- exclude: [
37
- "node_modules/**",
38
- "@shadcn/**",
39
- "**/components/ui/**"
40
- ],
30
+ exclude: ["node_modules/**", "@shadcn/**", "**/components/ui/**"],
41
31
  depth: 10,
42
32
  limit: 200,
43
33
  },
44
- debug: {
45
- verbose: false,
46
- showSummary: true,
47
- },
48
34
  };
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MD_FOOTER = exports.TEXT_HEADERS = exports.LANG_MAP = void 0;
2
7
  // ================================================================
3
8
  // 🧩 Prodex — Render Constants
4
9
  // Defines shared constants for renderer outDir formats.
5
10
  // ================================================================
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.TEXT_HEADERS = exports.LANG_MAP = void 0;
11
+ const package_json_1 = __importDefault(require("../../package.json"));
8
12
  exports.LANG_MAP = {
9
13
  "": "js",
10
14
  ".mjs": "js",
@@ -13,11 +17,12 @@ exports.LANG_MAP = {
13
17
  ".tsx": "tsx",
14
18
  ".php": "php",
15
19
  ".json": "json",
16
- ".d.ts": "ts"
20
+ ".d.ts": "ts",
17
21
  };
18
22
  exports.TEXT_HEADERS = {
19
23
  toc: "##==== Combined Scope ====",
20
- path: p => `##==== path: ${p} ====`,
21
- regionStart: p => `##region ${p}`,
22
- regionEnd: "##endregion"
24
+ path: (p) => `##==== path: ${p} ====`,
25
+ regionStart: (p) => `##region ${p}`,
26
+ regionEnd: "##endregion",
23
27
  };
28
+ exports.MD_FOOTER = ["\n---", "*Generated with [Prodex](https://github.com/emxhive/prodex) — Codebase decoded.*", `<!-- PRODEx v${package_json_1.default.version} | ${new Date().toISOString()} -->`].join("\n");
@@ -1,74 +1,38 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.runCombine = runCombine;
7
- const fs_1 = __importDefault(require("fs"));
8
- const inquirer_1 = __importDefault(require("inquirer"));
9
- const path_1 = __importDefault(require("path"));
10
4
  const picker_1 = require("../cli/picker");
11
5
  const summary_1 = require("../cli/summary");
12
6
  const dependency_1 = require("./dependency");
13
7
  const file_utils_1 = require("./file-utils");
14
- const renderers_1 = require("./renderers");
15
8
  const logger_1 = require("../lib/logger");
16
- const package_json_1 = __importDefault(require("../../package.json"));
17
- async function runCombine(cfg, showUi) {
18
- const { output, entry, resolve } = cfg;
19
- const toMd = cfg.output.format == "md";
20
- const ROOT = cfg.root;
21
- const { ui } = entry;
22
- const entryFiles = entry.files ?? [];
23
- let entries = [];
9
+ const output_1 = require("./output");
10
+ async function runCombine({ cfg, opts }) {
11
+ const { showUi, cliName } = opts;
12
+ let entries = (await resolveEntries(showUi, cfg)) ?? [];
13
+ if (!entries.length) {
14
+ logger_1.logger.error("No entries selected.");
15
+ return;
16
+ }
17
+ (0, summary_1.entrySummary)(entries);
18
+ const result = await (0, dependency_1.followChain)(entries, cfg);
19
+ const withinclude = await (0, dependency_1.applyincludes)(cfg, result.files);
20
+ const autoName = (0, file_utils_1.produceSmartName)(entries);
21
+ const outputPath = await (0, output_1.produceOutput)({ name: cliName ?? autoName, files: withinclude, cfg, showUi });
22
+ (0, summary_1.endSummary)(outputPath, result);
23
+ }
24
+ async function resolveEntries(showUi, cfg) {
25
+ const { root, entry } = cfg;
26
+ const { files } = entry;
24
27
  if (!showUi) {
25
28
  logger_1.logger.info("CI Mode");
26
- if (!entryFiles.length) {
29
+ if (!files?.length) {
27
30
  logger_1.logger.warn("No entry files defined and UI mode is disabled.");
28
- return;
31
+ process.exit(1);
29
32
  }
30
- entries = (await (0, file_utils_1.globScan)(entryFiles, { cwd: ROOT })).files;
33
+ return (await (0, file_utils_1.globScan)(files, { cwd: root })).files;
31
34
  }
32
35
  else {
33
- entries = await (0, picker_1.pickEntries)(ui.roots, ui.scanDepth, cfg);
34
- }
35
- if (!entries.length) {
36
- logger_1.logger.error("No entries selected.");
37
- return;
38
- }
39
- logger_1.logger.log("\n📋 You selected:");
40
- for (const e of entries)
41
- logger_1.logger.log(" -", e.replace(ROOT + "/", ""));
42
- const autoName = (0, file_utils_1.generateOutputName)(entries);
43
- const outDir = output.dir;
44
- const limit = resolve.limit;
45
- let outputBase = autoName;
46
- if (showUi) {
47
- const { outputBase: answer } = await inquirer_1.default.prompt([
48
- {
49
- type: "input",
50
- name: "outputBase",
51
- message: "Output file name (without extension):",
52
- default: autoName,
53
- filter: (v) => (v.trim() || autoName).replace(/[<>:"/\\|?*]+/g, "_"),
54
- },
55
- ]);
56
- outputBase = answer;
36
+ return await (0, picker_1.pickEntries)(cfg);
57
37
  }
58
- try {
59
- fs_1.default.mkdirSync(outDir, { recursive: true });
60
- }
61
- catch {
62
- logger_1.logger.warn("Could not create outDir directory:", outDir);
63
- }
64
- const outputPath = (0, file_utils_1.resolveOutDirPath)(outDir, outputBase, toMd);
65
- (0, summary_1.showSummary)({ outDir, fileName: path_1.default.basename(outputPath), entries });
66
- const result = await (0, dependency_1.followChain)(entries, cfg, limit);
67
- const withinclude = await (0, dependency_1.applyincludes)(cfg, result.files);
68
- const sorted = [...withinclude].sort((a, b) => a.localeCompare(b));
69
- const footer = ["\n---", "*Generated with [Prodex](https://github.com/emxhive/prodex) — Codebase decoded.*", `<!-- PRODEx v${package_json_1.default.version} | ${new Date().toISOString()} -->`].join("\n");
70
- const content = toMd ? [(0, renderers_1.tocTxt)(sorted), ...sorted.map(renderers_1.renderTxt)].join("") : [(0, renderers_1.tocMd)(sorted), ...sorted.map((f, i) => (0, renderers_1.renderMd)(f, i)), footer].join("\n");
71
- fs_1.default.writeFileSync(outputPath, content, "utf8");
72
- logger_1.logger.log(`\n✅ ${outputPath.norm()}`);
73
- (0, summary_1.importSummary)(result);
74
38
  }
@@ -9,7 +9,9 @@ const path_1 = __importDefault(require("path"));
9
9
  const config_1 = require("../constants/config");
10
10
  const file_utils_1 = require("./file-utils");
11
11
  const logger_1 = require("../lib/logger");
12
- async function followChain(entryFiles, cfg, limit = 200) {
12
+ const utils_1 = require("../lib/utils");
13
+ async function followChain(entryFiles, cfg) {
14
+ const limit = cfg.resolve.limit;
13
15
  logger_1.logger.debug("🧩 Following dependency chain...");
14
16
  const visited = new Set();
15
17
  const all = [];
@@ -37,15 +39,17 @@ async function followChain(entryFiles, cfg, limit = 200) {
37
39
  }
38
40
  }
39
41
  return {
40
- files: [...new Set(all)],
42
+ files: (0, utils_1.unique)(all),
41
43
  stats: { expected, resolved },
42
44
  };
43
45
  }
44
46
  async function applyincludes(cfg, files) {
47
+ ;
45
48
  const { resolve } = cfg;
46
49
  const ROOT = cfg.root;
47
50
  const scan = await (0, file_utils_1.globScan)(resolve.include, { cwd: ROOT });
48
51
  logger_1.logger.debug("APPLY_include", _2j(scan));
49
- const combined = [...new Set([...files, ...scan.files])];
52
+ const combined = (0, utils_1.unique)([...files, ...scan.files]);
53
+ ;
50
54
  return combined;
51
55
  }
@@ -4,32 +4,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.globScan = globScan;
7
- exports.generateOutputName = generateOutputName;
8
- exports.resolveOutDirPath = resolveOutDirPath;
7
+ exports.produceSmartName = produceSmartName;
9
8
  const path_1 = __importDefault(require("path"));
10
9
  const fast_glob_1 = __importDefault(require("fast-glob"));
11
10
  const config_1 = require("../constants/config");
12
11
  const logger_1 = require("../lib/logger");
12
+ const utils_1 = require("../lib/utils");
13
13
  /**
14
14
  * Safe micromatch.scan wrapper (compatible with micromatch v4 & v5)
15
15
  */
16
- async function globScan(pattern, opts) {
17
- logger_1.logger.debug("PATTERN", pattern);
16
+ async function globScan(patterns, opts) {
17
+ logger_1.logger.debug("PATTERNS", patterns);
18
18
  const { absolute = true, cwd = process.cwd() } = opts;
19
- if (!pattern?.length)
19
+ if (!patterns?.length)
20
20
  return { files: [] };
21
- const files = await (0, fast_glob_1.default)(pattern, {
21
+ const files = (await (0, fast_glob_1.default)(patterns, {
22
22
  cwd,
23
+ extglob: true,
23
24
  dot: true,
24
25
  onlyFiles: true,
25
26
  ignore: config_1.GLOBAL_IGNORE,
26
27
  absolute,
27
- });
28
+ })).map((f) => path_1.default.resolve(f));
28
29
  logger_1.logger.debug("GLOB-SCAN_FILES ", _2j(files));
29
30
  return { files };
30
31
  }
31
- function generateOutputName(entries) {
32
- const names = entries.map((f) => path_1.default.basename(f, path_1.default.extname(f)));
32
+ function produceSmartName(entries) {
33
+ const names = (0, utils_1.unique)(entries.map((f) => path_1.default.basename(f, path_1.default.extname(f))));
33
34
  if (names.length === 1)
34
35
  return names[0];
35
36
  if (names.length === 2)
@@ -38,7 +39,3 @@ function generateOutputName(entries) {
38
39
  return `${names[0]}-and-${names.length - 1}more`;
39
40
  return "unknown";
40
41
  }
41
- function resolveOutDirPath(outDir, base, asTxt = false) {
42
- const ext = asTxt ? "txt" : "md";
43
- return path_1.default.join(outDir, `${base}-combined.${ext}`);
44
- }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.produceOutput = produceOutput;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const prompt_1 = require("../lib/prompt");
10
+ const renderers_1 = require("./renderers");
11
+ const logger_1 = require("../lib/logger");
12
+ const utils_1 = require("../lib/utils");
13
+ const questions_1 = require("../lib/questions");
14
+ const render_constants_1 = require("../constants/render-constants");
15
+ const config_1 = require("../constants/config");
16
+ /**
17
+ * 🧩 produceOutput()
18
+ * Handles rendering and writing of the final trace file.
19
+ * Receives resolved files and configuration from combine.ts.
20
+ */
21
+ async function produceOutput({ name, files, cfg, showUi }) {
22
+ const { output: { format, versioned, dir }, } = cfg;
23
+ ;
24
+ // 1️⃣ Determine base filename
25
+ let outputBase = name;
26
+ if (showUi) {
27
+ const result = await (0, prompt_1.prompt)(questions_1.OUTPUT_NAME_QUESTION);
28
+ if (result?.outputBase)
29
+ outputBase = result.outputBase;
30
+ }
31
+ // 2️⃣ Prefix timestamp if versioned
32
+ outputBase = `${outputBase}-${config_1.SUFFIX}`;
33
+ if (versioned)
34
+ outputBase = `${outputBase}_${(0, utils_1.shortTimestamp)()}`;
35
+ // 3️⃣ Ensure output directory
36
+ try {
37
+ fs_1.default.mkdirSync(dir, { recursive: true });
38
+ }
39
+ catch {
40
+ logger_1.logger.warn("Could not create dir directory:", dir);
41
+ }
42
+ // 4️⃣ Prepare and write content
43
+ const outputPath = path_1.default.join(dir, `${outputBase}.${format}`);
44
+ const sorted = [...files].sort((a, b) => a.localeCompare(b));
45
+ const content = format === "txt" ? [(0, renderers_1.tocTxt)(sorted), ...sorted.map(renderers_1.renderTxt)].join("") : [(0, renderers_1.tocMd)(sorted), ...sorted.map((f, i) => (0, renderers_1.renderMd)(f, i)), render_constants_1.MD_FOOTER].join("\n");
46
+ fs_1.default.writeFileSync(outputPath, content, "utf8");
47
+ return outputPath;
48
+ }
package/dist/index.js CHANGED
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = startProdex;
4
4
  const combine_1 = require("./core/combine");
5
5
  const init_1 = require("./cli/init");
6
- const logger_1 = require("./lib/logger");
7
6
  const cli_input_1 = require("./cli/cli-input");
8
7
  const config_loader_1 = require("./constants/config-loader");
9
8
  require("./lib/polyfills");
9
+ const summary_1 = require("./cli/summary");
10
10
  async function startProdex() {
11
11
  const args = process.argv;
12
12
  // Handle init mode
@@ -14,16 +14,13 @@ async function startProdex() {
14
14
  return (0, init_1.initProdex)();
15
15
  }
16
16
  // Parse CLI input
17
- const { root, flags, warnings, errors } = (0, cli_input_1.parseCliInput)(args);
18
- if (warnings.length)
19
- logger_1.logger.warn("Warnings:", warnings);
20
- if (errors.length)
21
- logger_1.logger.error("Errors:", errors);
17
+ const { root, flags } = (0, cli_input_1.parseCliInput)(args);
22
18
  // Load and merge configuration (with flag overrides)
23
19
  const config = await (0, config_loader_1.loadProdexConfig)(flags, root);
24
- logger_1.logger.log(`------- PRODEx RUN @ ${new Date().toLocaleTimeString()} Codebase decoded -------`);
25
- // Log parse results for testing
26
- logger_1.logger.debug("🧩 Parsed CLI input:", _2j({ flags }));
27
- logger_1.logger.debug("Final merged config:", _2j(config));
28
- await (0, combine_1.runCombine)(config, !flags.ci && !flags?.files?.length);
20
+ (0, summary_1.introSummary)({ flags, config });
21
+ const opts = {
22
+ showUi: !flags.ci && !flags?.files?.length && !config?.entry?.ui?.enablePicker,
23
+ cliName: config.name,
24
+ };
25
+ await (0, combine_1.runCombine)({ cfg: config, opts });
29
26
  }
@@ -8,7 +8,7 @@ const env = () => {
8
8
  exports.logger = {
9
9
  debug: (...args) => !env().silent && env().debug && console.log("\n🪶 [debug]", ...args),
10
10
  info: (...args) => !env().silent && console.log("\n📌 [info]", ...args),
11
- warn: (...args) => !env().silent && console.warn("\n⚠️ [warn]", ...args),
11
+ warn: (...args) => !env().silent && console.warn("\n⚠️ [warn]", ...args),
12
12
  error: (...args) => !env().silent && console.error("\n💥 [error]", ...args),
13
13
  log: (...args) => !env().silent && console.log("\n", ...args),
14
14
  };
@@ -9,4 +9,19 @@ if (!String.prototype.norm) {
9
9
  return this.replace(/\\/g, "/");
10
10
  };
11
11
  }
12
+ if (!String.prototype.clean) {
13
+ String.prototype.clean = function () {
14
+ return this.replace(/[<>:\"/\\|?*]+/g, "_");
15
+ };
16
+ }
12
17
  globalThis._2j = (obj) => util_1.default.inspect(obj, { colors: true, depth: null, breakLength: 150, compact: 3 });
18
+ globalThis._bpt = function (param) {
19
+ if (process.env.PRODEX_DEBUG !== "1")
20
+ return;
21
+ console.log("⭕ BREAKPOINT");
22
+ if (typeof param === "function")
23
+ param();
24
+ else
25
+ console.log(_2j(param));
26
+ process.exit(1);
27
+ };
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.prompt = prompt;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const logger_1 = require("./logger");
9
+ /**
10
+ * 🧩 prompt()
11
+ * Unified and safe wrapper for inquirer.prompt()
12
+ *
13
+ * - Requires explicit `showUi` flag (no env auto-detection).
14
+ * - Returns `null` or `fallback` on failure or disabled UI.
15
+ * - Handles TTY errors and user cancellations gracefully.
16
+ */
17
+ async function prompt(questions, fallback) {
18
+ try {
19
+ const answers = (await inquirer_1.default.prompt(questions));
20
+ return answers;
21
+ }
22
+ catch (err) {
23
+ if (err?.isTtyError) {
24
+ logger_1.logger.warn("Interactive prompts not supported (no TTY).");
25
+ }
26
+ else if (/canceled|aborted/i.test(err?.message)) {
27
+ logger_1.logger.warn("Prompt canceled by user.");
28
+ }
29
+ else {
30
+ logger_1.logger.error("Prompt failed:", err.message || err);
31
+ }
32
+ return fallback ?? null;
33
+ }
34
+ }