@releasekit/notes 0.7.10 → 0.7.12

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.
@@ -2,8 +2,11 @@ import {
2
2
  EXIT_CODES,
3
3
  ReleaseKitError,
4
4
  debug,
5
+ error,
5
6
  formatVersion,
6
7
  info,
8
+ setLogLevel,
9
+ setQuietMode,
7
10
  success,
8
11
  warn,
9
12
  writeMarkdown
@@ -404,16 +407,16 @@ function loadConfigFile(configPath) {
404
407
  const parsed = parseJsonc(content);
405
408
  const substituted = substituteInObject(parsed);
406
409
  return ReleaseKitConfigSchema.parse(substituted);
407
- } catch (error) {
408
- if (error instanceof z2.ZodError) {
409
- const issues = error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
410
+ } catch (error2) {
411
+ if (error2 instanceof z2.ZodError) {
412
+ const issues = error2.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
410
413
  throw new ConfigError(`Config validation errors:
411
414
  ${issues}`);
412
415
  }
413
- if (error instanceof SyntaxError) {
414
- throw new ConfigError(`Invalid JSON in config file: ${error.message}`);
416
+ if (error2 instanceof SyntaxError) {
417
+ throw new ConfigError(`Invalid JSON in config file: ${error2.message}`);
415
418
  }
416
- throw error;
419
+ throw error2;
417
420
  }
418
421
  }
419
422
  function loadConfig(options) {
@@ -466,8 +469,8 @@ var ConfigError2 = class extends NotesError {
466
469
  "Run releasekit-notes init to create default config"
467
470
  ];
468
471
  };
469
- function getExitCode(error) {
470
- switch (error.code) {
472
+ function getExitCode(error2) {
473
+ switch (error2.code) {
471
474
  case "CONFIG_ERROR":
472
475
  return EXIT_CODES.CONFIG_ERROR;
473
476
  case "INPUT_PARSE_ERROR":
@@ -506,8 +509,8 @@ function parseVersionOutput(json) {
506
509
  let data;
507
510
  try {
508
511
  data = JSON.parse(json);
509
- } catch (error) {
510
- throw new InputParseError(`Invalid JSON input: ${error instanceof Error ? error.message : String(error)}`);
512
+ } catch (error2) {
513
+ throw new InputParseError(`Invalid JSON input: ${error2 instanceof Error ? error2.message : String(error2)}`);
511
514
  }
512
515
  if (!data.changelogs || !Array.isArray(data.changelogs)) {
513
516
  throw new InputParseError('Input must contain a "changelogs" array');
@@ -630,9 +633,9 @@ var AnthropicProvider = class extends BaseLLMProvider {
630
633
  throw new LLMError("Unexpected response format from Anthropic");
631
634
  }
632
635
  return firstBlock.text;
633
- } catch (error) {
634
- if (error instanceof LLMError) throw error;
635
- throw new LLMError(`Anthropic API error: ${error instanceof Error ? error.message : String(error)}`);
636
+ } catch (error2) {
637
+ if (error2 instanceof LLMError) throw error2;
638
+ throw new LLMError(`Anthropic API error: ${error2 instanceof Error ? error2.message : String(error2)}`);
636
639
  }
637
640
  }
638
641
  };
@@ -688,9 +691,9 @@ var OllamaProvider = class extends BaseLLMProvider {
688
691
  throw new LLMError("Empty response from Ollama");
689
692
  }
690
693
  return data.message.content;
691
- } catch (error) {
692
- if (error instanceof LLMError) throw error;
693
- throw new LLMError(`Ollama error: ${error instanceof Error ? error.message : String(error)}`);
694
+ } catch (error2) {
695
+ if (error2 instanceof LLMError) throw error2;
696
+ throw new LLMError(`Ollama error: ${error2 instanceof Error ? error2.message : String(error2)}`);
694
697
  }
695
698
  }
696
699
  };
@@ -726,9 +729,9 @@ var OpenAIProvider = class extends BaseLLMProvider {
726
729
  throw new LLMError("Empty response from OpenAI");
727
730
  }
728
731
  return content;
729
- } catch (error) {
730
- if (error instanceof LLMError) throw error;
731
- throw new LLMError(`OpenAI API error: ${error instanceof Error ? error.message : String(error)}`);
732
+ } catch (error2) {
733
+ if (error2 instanceof LLMError) throw error2;
734
+ throw new LLMError(`OpenAI API error: ${error2 instanceof Error ? error2.message : String(error2)}`);
732
735
  }
733
736
  }
734
737
  };
@@ -761,9 +764,9 @@ var OpenAICompatibleProvider = class extends BaseLLMProvider {
761
764
  throw new LLMError("Empty response from LLM");
762
765
  }
763
766
  return content;
764
- } catch (error) {
765
- if (error instanceof LLMError) throw error;
766
- throw new LLMError(`LLM API error: ${error instanceof Error ? error.message : String(error)}`);
767
+ } catch (error2) {
768
+ if (error2 instanceof LLMError) throw error2;
769
+ throw new LLMError(`LLM API error: ${error2 instanceof Error ? error2.message : String(error2)}`);
767
770
  }
768
771
  }
769
772
  };
@@ -938,9 +941,9 @@ async function categorizeEntries(provider, entries, context) {
938
941
  }
939
942
  }
940
943
  return result;
941
- } catch (error) {
944
+ } catch (error2) {
942
945
  warn(
943
- `LLM categorization failed, falling back to General: ${error instanceof Error ? error.message : String(error)}`
946
+ `LLM categorization failed, falling back to General: ${error2 instanceof Error ? error2.message : String(error2)}`
944
947
  );
945
948
  return [{ category: "General", entries: entriesCopy }];
946
949
  }
@@ -1002,8 +1005,8 @@ async function withRetry(fn, options = {}) {
1002
1005
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
1003
1006
  try {
1004
1007
  return await fn();
1005
- } catch (error) {
1006
- lastError = error;
1008
+ } catch (error2) {
1009
+ lastError = error2;
1007
1010
  if (attempt < maxAttempts - 1) {
1008
1011
  const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
1009
1012
  const jitter = base * 0.2 * (Math.random() * 2 - 1);
@@ -1097,9 +1100,9 @@ async function enhanceAndCategorize(provider, entries, context) {
1097
1100
  }
1098
1101
  return { enhancedEntries: validatedEntries, categories };
1099
1102
  }, retryOpts);
1100
- } catch (error) {
1103
+ } catch (error2) {
1101
1104
  warn(
1102
- `Combined enhance+categorize failed after ${retryOpts.maxAttempts} attempts: ${error instanceof Error ? error.message : String(error)}`
1105
+ `Combined enhance+categorize failed after ${retryOpts.maxAttempts} attempts: ${error2 instanceof Error ? error2.message : String(error2)}`
1103
1106
  );
1104
1107
  return {
1105
1108
  enhancedEntries: entries,
@@ -1214,8 +1217,8 @@ import ejs from "ejs";
1214
1217
  function renderEjs(template, context) {
1215
1218
  try {
1216
1219
  return ejs.render(template, context);
1217
- } catch (error) {
1218
- throw new TemplateError(`EJS render error: ${error instanceof Error ? error.message : String(error)}`);
1220
+ } catch (error2) {
1221
+ throw new TemplateError(`EJS render error: ${error2 instanceof Error ? error2.message : String(error2)}`);
1219
1222
  }
1220
1223
  }
1221
1224
  function renderEjsFile(filePath, context) {
@@ -1249,8 +1252,8 @@ function renderHandlebars(template, context) {
1249
1252
  try {
1250
1253
  const compiled = Handlebars.compile(template);
1251
1254
  return compiled(context);
1252
- } catch (error) {
1253
- throw new TemplateError(`Handlebars render error: ${error instanceof Error ? error.message : String(error)}`);
1255
+ } catch (error2) {
1256
+ throw new TemplateError(`Handlebars render error: ${error2 instanceof Error ? error2.message : String(error2)}`);
1254
1257
  }
1255
1258
  }
1256
1259
  function renderHandlebarsFile(filePath, context) {
@@ -1277,8 +1280,8 @@ function renderHandlebarsComposable(templateDir, context) {
1277
1280
  try {
1278
1281
  const compiled = Handlebars.compile(fs6.readFileSync(documentPath, "utf-8"));
1279
1282
  return compiled(context);
1280
- } catch (error) {
1281
- throw new TemplateError(`Handlebars render error: ${error instanceof Error ? error.message : String(error)}`);
1283
+ } catch (error2) {
1284
+ throw new TemplateError(`Handlebars render error: ${error2 instanceof Error ? error2.message : String(error2)}`);
1282
1285
  }
1283
1286
  }
1284
1287
 
@@ -1297,8 +1300,8 @@ function renderLiquid(template, context) {
1297
1300
  const engine = createLiquidEngine();
1298
1301
  try {
1299
1302
  return engine.renderSync(engine.parse(template), context);
1300
- } catch (error) {
1301
- throw new TemplateError(`Liquid render error: ${error instanceof Error ? error.message : String(error)}`);
1303
+ } catch (error2) {
1304
+ throw new TemplateError(`Liquid render error: ${error2 instanceof Error ? error2.message : String(error2)}`);
1302
1305
  }
1303
1306
  }
1304
1307
  function renderLiquidFile(filePath, context) {
@@ -1316,8 +1319,8 @@ function renderLiquidComposable(templateDir, context) {
1316
1319
  const engine = createLiquidEngine(templateDir);
1317
1320
  try {
1318
1321
  return engine.renderFileSync("document", context);
1319
- } catch (error) {
1320
- throw new TemplateError(`Liquid render error: ${error instanceof Error ? error.message : String(error)}`);
1322
+ } catch (error2) {
1323
+ throw new TemplateError(`Liquid render error: ${error2 instanceof Error ? error2.message : String(error2)}`);
1321
1324
  }
1322
1325
  }
1323
1326
 
@@ -1619,8 +1622,8 @@ async function processWithLLM(context, llmConfig) {
1619
1622
  ...context,
1620
1623
  enhanced
1621
1624
  };
1622
- } catch (error) {
1623
- warn(`LLM processing failed: ${error instanceof Error ? error.message : String(error)}`);
1625
+ } catch (error2) {
1626
+ warn(`LLM processing failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
1624
1627
  warn("Falling back to raw entries");
1625
1628
  return context;
1626
1629
  }
@@ -1699,8 +1702,8 @@ async function runPipeline(input, config, dryRun) {
1699
1702
  const monoFiles = await writeMonorepoFiles(contexts, config, dryRun, changelogConfig.file ?? "CHANGELOG.md");
1700
1703
  files.push(...monoFiles);
1701
1704
  }
1702
- } catch (error) {
1703
- warn(`Failed to write changelog: ${error instanceof Error ? error.message : String(error)}`);
1705
+ } catch (error2) {
1706
+ warn(`Failed to write changelog: ${error2 instanceof Error ? error2.message : String(error2)}`);
1704
1707
  }
1705
1708
  }
1706
1709
  if (releaseNotesConfig?.mode) {
@@ -1731,8 +1734,8 @@ async function runPipeline(input, config, dryRun) {
1731
1734
  );
1732
1735
  files.push(...monoFiles);
1733
1736
  }
1734
- } catch (error) {
1735
- warn(`Failed to write release notes: ${error instanceof Error ? error.message : String(error)}`);
1737
+ } catch (error2) {
1738
+ warn(`Failed to write release notes: ${error2 instanceof Error ? error2.message : String(error2)}`);
1736
1739
  }
1737
1740
  }
1738
1741
  const packageNotes = {};
@@ -1795,10 +1798,202 @@ async function writeMonorepoFiles(contexts, config, dryRun, fileName) {
1795
1798
  return monoFiles;
1796
1799
  }
1797
1800
 
1801
+ // src/command.ts
1802
+ import * as fs10 from "fs";
1803
+ import * as readline from "readline";
1804
+ import { Command } from "commander";
1805
+ function createNotesCommand() {
1806
+ const cmd = new Command("notes").description(
1807
+ "Generate changelogs with LLM-powered enhancement and flexible templating"
1808
+ );
1809
+ cmd.command("generate", { isDefault: true }).description("Generate changelog from input data").option("-i, --input <file>", "Input file (default: stdin)").option("--no-changelog", "Disable changelog generation").option("--changelog-mode <mode>", "Changelog location mode (root|packages|both)").option("--changelog-file <name>", "Changelog file name override").option("--release-notes-mode <mode>", "Enable release notes and set location (root|packages|both)").option("--release-notes-file <name>", "Release notes file name override").option("--no-release-notes", "Disable release notes generation").option("-t, --template <path>", "Template file or directory").option("-e, --engine <engine>", "Template engine (handlebars|liquid|ejs)").option("--monorepo <mode>", "Monorepo mode (root|packages|both)").option("--llm-provider <provider>", "LLM provider").option("--llm-model <model>", "LLM model").option("--llm-base-url <url>", "LLM base URL (for openai-compatible provider)").option("--llm-tasks <tasks>", "Comma-separated LLM tasks").option("--no-llm", "Disable LLM processing").option("--target <package>", "Filter to a specific package name").option("--config <path>", "Config file path").option("--regenerate", "Regenerate entire changelog instead of prepending new entries").option("--dry-run", "Preview without writing").option("-v, --verbose", "Increase verbosity", increaseVerbosity, 0).option("-q, --quiet", "Suppress non-error output").action(async (options) => {
1810
+ setVerbosity(options.verbose);
1811
+ if (options.quiet) setQuietMode(true);
1812
+ try {
1813
+ const loadedConfig = loadConfig({ cwd: process.cwd(), configPath: options.config });
1814
+ const config = loadedConfig?.notes ?? {};
1815
+ if (loadedConfig?.monorepo && !config.monorepo) {
1816
+ config.monorepo = loadedConfig.monorepo;
1817
+ }
1818
+ if (options.changelog === false) {
1819
+ config.changelog = false;
1820
+ } else if (options.changelogMode || options.changelogFile) {
1821
+ const existing = config.changelog !== false ? config.changelog ?? {} : {};
1822
+ const mode = options.changelogMode ?? existing.mode ?? "root";
1823
+ config.changelog = {
1824
+ ...existing,
1825
+ mode,
1826
+ ...options.changelogFile ? { file: options.changelogFile } : {}
1827
+ };
1828
+ }
1829
+ if (options.releaseNotes === false) {
1830
+ config.releaseNotes = false;
1831
+ } else if (options.releaseNotesMode || options.releaseNotesFile) {
1832
+ const existing = config.releaseNotes !== false ? config.releaseNotes ?? {} : {};
1833
+ const mode = options.releaseNotesMode ?? existing.mode ?? "root";
1834
+ config.releaseNotes = {
1835
+ ...existing,
1836
+ mode,
1837
+ ...options.releaseNotesFile ? { file: options.releaseNotesFile } : {}
1838
+ };
1839
+ }
1840
+ if (config.changelog === false && (options.template || options.engine)) {
1841
+ const ignored = [options.template && "--template", options.engine && "--engine"].filter(Boolean).join(" and ");
1842
+ warn(`${ignored} ignored: changelog is disabled via --no-changelog`);
1843
+ }
1844
+ if (options.template && config.changelog !== false) {
1845
+ const existing = config.changelog ?? {};
1846
+ config.changelog = {
1847
+ ...existing,
1848
+ templates: { ...existing.templates, path: options.template }
1849
+ };
1850
+ }
1851
+ if (options.engine && config.changelog !== false) {
1852
+ const existing = config.changelog ?? {};
1853
+ config.changelog = {
1854
+ ...existing,
1855
+ templates: { ...existing.templates, engine: options.engine }
1856
+ };
1857
+ }
1858
+ if (options.regenerate) {
1859
+ config.updateStrategy = "regenerate";
1860
+ }
1861
+ if (options.llm === false) {
1862
+ if (config.releaseNotes && typeof config.releaseNotes !== "boolean" && config.releaseNotes.llm) {
1863
+ info("LLM processing disabled via --no-llm flag");
1864
+ config.releaseNotes = { ...config.releaseNotes, llm: void 0 };
1865
+ }
1866
+ } else if (options.llmProvider || options.llmModel || options.llmBaseUrl || options.llmTasks) {
1867
+ if (config.releaseNotes === false) {
1868
+ warn("--llm-* flags ignored: release notes are disabled via --no-release-notes");
1869
+ } else {
1870
+ const existingRn = typeof config.releaseNotes === "object" ? config.releaseNotes : {};
1871
+ const existingLlm = existingRn.llm;
1872
+ const llm = {
1873
+ provider: existingLlm?.provider ?? "openai-compatible",
1874
+ model: existingLlm?.model ?? "",
1875
+ ...existingLlm ?? {}
1876
+ };
1877
+ if (options.llmProvider) llm.provider = options.llmProvider;
1878
+ if (options.llmModel) llm.model = options.llmModel;
1879
+ if (options.llmBaseUrl) llm.baseURL = options.llmBaseUrl;
1880
+ if (options.llmTasks) {
1881
+ const taskNames = options.llmTasks.split(",").map((t) => t.trim());
1882
+ llm.tasks = {
1883
+ enhance: taskNames.includes("enhance"),
1884
+ summarize: taskNames.includes("summarize"),
1885
+ categorize: taskNames.includes("categorize"),
1886
+ releaseNotes: taskNames.includes("release-notes") || taskNames.includes("releaseNotes")
1887
+ };
1888
+ }
1889
+ config.releaseNotes = {
1890
+ ...existingRn,
1891
+ llm
1892
+ };
1893
+ info(`LLM configured: ${llm.provider}${llm.model ? ` (${llm.model})` : ""}`);
1894
+ if (llm.baseURL) {
1895
+ info(`LLM base URL: ${llm.baseURL}`);
1896
+ }
1897
+ const taskList = Object.entries(llm.tasks || {}).filter(([, enabled]) => enabled).map(([name]) => name).join(", ");
1898
+ if (taskList) {
1899
+ info(`LLM tasks: ${taskList}`);
1900
+ }
1901
+ }
1902
+ }
1903
+ let inputJson;
1904
+ if (options.input) {
1905
+ inputJson = fs10.readFileSync(options.input, "utf-8");
1906
+ } else {
1907
+ inputJson = await readStdin();
1908
+ }
1909
+ const input = parseVersionOutput(inputJson);
1910
+ if (options.target) {
1911
+ const before = input.packages.length;
1912
+ input.packages = input.packages.filter((p) => p.packageName === options.target);
1913
+ if (input.packages.length === 0) {
1914
+ info(`No changelog found for package "${options.target}" (had ${before} package(s))`);
1915
+ return;
1916
+ }
1917
+ info(`Filtered to package: ${options.target}`);
1918
+ }
1919
+ if (options.monorepo) {
1920
+ const monoMode = options.monorepo;
1921
+ config.monorepo = { ...config.monorepo, mode: monoMode };
1922
+ if (config.changelog !== false && !options.changelogMode) {
1923
+ config.changelog = { ...config.changelog ?? {}, mode: monoMode };
1924
+ }
1925
+ if (config.releaseNotes !== false && config.releaseNotes !== void 0 && !options.releaseNotesMode) {
1926
+ config.releaseNotes = { ...config.releaseNotes, mode: monoMode };
1927
+ }
1928
+ }
1929
+ await runPipeline(input, config, options.dryRun ?? false);
1930
+ if (options.dryRun) {
1931
+ info("Dry run complete - no files were written");
1932
+ } else {
1933
+ success("Changelog generation complete");
1934
+ }
1935
+ } catch (err) {
1936
+ handleError(err);
1937
+ }
1938
+ });
1939
+ cmd.command("auth <provider>").description("Configure API key for an LLM provider").option("--key <key>", "API key (omit to be prompted)").action(async (provider, options) => {
1940
+ let apiKey;
1941
+ if (options.key) {
1942
+ apiKey = options.key;
1943
+ } else {
1944
+ apiKey = await promptSecret(`Enter API key for ${provider}: `);
1945
+ }
1946
+ if (!apiKey.trim()) {
1947
+ error("API key cannot be empty");
1948
+ process.exit(EXIT_CODES.GENERAL_ERROR);
1949
+ }
1950
+ saveAuth(provider, apiKey.trim());
1951
+ success(`API key saved for ${provider}`);
1952
+ });
1953
+ cmd.command("providers").description("List available LLM providers").action(() => {
1954
+ info("Available LLM providers:");
1955
+ console.log(" openai - OpenAI (GPT models)");
1956
+ console.log(" anthropic - Anthropic (Claude models)");
1957
+ console.log(" ollama - Ollama (local models)");
1958
+ console.log(" openai-compatible - Any OpenAI-compatible endpoint");
1959
+ });
1960
+ return cmd;
1961
+ }
1962
+ function increaseVerbosity(_, previous) {
1963
+ return previous + 1;
1964
+ }
1965
+ function setVerbosity(level) {
1966
+ const levels = ["info", "debug", "trace"];
1967
+ setLogLevel(levels[Math.min(level, levels.length - 1)] ?? "info");
1968
+ }
1969
+ async function readStdin() {
1970
+ const chunks = [];
1971
+ for await (const chunk of process.stdin) {
1972
+ chunks.push(chunk);
1973
+ }
1974
+ return chunks.join("");
1975
+ }
1976
+ function promptSecret(prompt) {
1977
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1978
+ return new Promise((resolve2) => {
1979
+ rl.question(prompt, (answer) => {
1980
+ rl.close();
1981
+ resolve2(answer);
1982
+ });
1983
+ });
1984
+ }
1985
+ function handleError(err) {
1986
+ if (err instanceof NotesError) {
1987
+ err.logError();
1988
+ process.exit(getExitCode(err));
1989
+ }
1990
+ error(err instanceof Error ? err.message : String(err));
1991
+ process.exit(EXIT_CODES.GENERAL_ERROR);
1992
+ }
1993
+
1798
1994
  export {
1799
1995
  loadAuth,
1800
1996
  saveAuth,
1801
- loadConfig,
1802
1997
  NotesError,
1803
1998
  InputParseError,
1804
1999
  TemplateError,
@@ -1809,9 +2004,10 @@ export {
1809
2004
  parseVersionOutput,
1810
2005
  parseVersionOutputFile,
1811
2006
  parseVersionOutputStdin,
1812
- loadConfig2,
2007
+ loadConfig2 as loadConfig,
1813
2008
  getDefaultConfig,
1814
2009
  createTemplateContext,
1815
2010
  runPipeline,
1816
- processInput
2011
+ processInput,
2012
+ createNotesCommand
1817
2013
  };
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from 'commander';
3
-
4
- declare function createNotesCommand(): Command;
5
-
6
- export { createNotesCommand };
2
+ export { createNotesCommand } from './command.ts';
3
+ import './core/config.ts';
4
+ import './core/pipeline.ts';
5
+ import './core/types.ts';
6
+ import './errors/index.ts';
7
+ import './input/version-output.ts';
8
+ import './monorepo/aggregator.ts';
9
+ import './output/json.ts';
10
+ import './output/markdown.ts';
package/dist/cli.js CHANGED
@@ -1,216 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- NotesError,
4
- getExitCode,
5
- loadConfig,
6
- parseVersionOutput,
7
- runPipeline,
8
- saveAuth
9
- } from "./chunk-FNIE2SHL.js";
3
+ createNotesCommand
4
+ } from "./chunk-Y4S5UWCL.js";
10
5
  import {
11
- EXIT_CODES,
12
- error,
13
- info,
14
- readPackageVersion,
15
- setLogLevel,
16
- setQuietMode,
17
- success,
18
- warn
6
+ readPackageVersion
19
7
  } from "./chunk-7TJSPQPW.js";
20
8
 
21
9
  // src/cli.ts
22
10
  import * as fs from "fs";
23
- import * as readline from "readline";
24
11
  import { fileURLToPath } from "url";
25
- import { Command } from "commander";
26
- function createNotesCommand() {
27
- const cmd = new Command("notes").description(
28
- "Generate changelogs with LLM-powered enhancement and flexible templating"
29
- );
30
- cmd.command("generate", { isDefault: true }).description("Generate changelog from input data").option("-i, --input <file>", "Input file (default: stdin)").option("--no-changelog", "Disable changelog generation").option("--changelog-mode <mode>", "Changelog location mode (root|packages|both)").option("--changelog-file <name>", "Changelog file name override").option("--release-notes-mode <mode>", "Enable release notes and set location (root|packages|both)").option("--release-notes-file <name>", "Release notes file name override").option("--no-release-notes", "Disable release notes generation").option("-t, --template <path>", "Template file or directory").option("-e, --engine <engine>", "Template engine (handlebars|liquid|ejs)").option("--monorepo <mode>", "Monorepo mode (root|packages|both)").option("--llm-provider <provider>", "LLM provider").option("--llm-model <model>", "LLM model").option("--llm-base-url <url>", "LLM base URL (for openai-compatible provider)").option("--llm-tasks <tasks>", "Comma-separated LLM tasks").option("--no-llm", "Disable LLM processing").option("--target <package>", "Filter to a specific package name").option("--config <path>", "Config file path").option("--regenerate", "Regenerate entire changelog instead of prepending new entries").option("--dry-run", "Preview without writing").option("-v, --verbose", "Increase verbosity", increaseVerbosity, 0).option("-q, --quiet", "Suppress non-error output").action(async (options) => {
31
- setVerbosity(options.verbose);
32
- if (options.quiet) setQuietMode(true);
33
- try {
34
- const loadedConfig = loadConfig({ cwd: process.cwd(), configPath: options.config });
35
- const config = loadedConfig?.notes ?? {};
36
- if (loadedConfig?.monorepo && !config.monorepo) {
37
- config.monorepo = loadedConfig.monorepo;
38
- }
39
- if (options.changelog === false) {
40
- config.changelog = false;
41
- } else if (options.changelogMode || options.changelogFile) {
42
- const existing = config.changelog !== false ? config.changelog ?? {} : {};
43
- const mode = options.changelogMode ?? existing.mode ?? "root";
44
- config.changelog = {
45
- ...existing,
46
- mode,
47
- ...options.changelogFile ? { file: options.changelogFile } : {}
48
- };
49
- }
50
- if (options.releaseNotes === false) {
51
- config.releaseNotes = false;
52
- } else if (options.releaseNotesMode || options.releaseNotesFile) {
53
- const existing = config.releaseNotes !== false ? config.releaseNotes ?? {} : {};
54
- const mode = options.releaseNotesMode ?? existing.mode ?? "root";
55
- config.releaseNotes = {
56
- ...existing,
57
- mode,
58
- ...options.releaseNotesFile ? { file: options.releaseNotesFile } : {}
59
- };
60
- }
61
- if (config.changelog === false && (options.template || options.engine)) {
62
- const ignored = [options.template && "--template", options.engine && "--engine"].filter(Boolean).join(" and ");
63
- warn(`${ignored} ignored: changelog is disabled via --no-changelog`);
64
- }
65
- if (options.template && config.changelog !== false) {
66
- const existing = config.changelog ?? {};
67
- config.changelog = {
68
- ...existing,
69
- templates: { ...existing.templates, path: options.template }
70
- };
71
- }
72
- if (options.engine && config.changelog !== false) {
73
- const existing = config.changelog ?? {};
74
- config.changelog = {
75
- ...existing,
76
- templates: { ...existing.templates, engine: options.engine }
77
- };
78
- }
79
- if (options.regenerate) {
80
- config.updateStrategy = "regenerate";
81
- }
82
- if (options.llm === false) {
83
- if (config.releaseNotes && typeof config.releaseNotes !== "boolean" && config.releaseNotes.llm) {
84
- info("LLM processing disabled via --no-llm flag");
85
- config.releaseNotes = { ...config.releaseNotes, llm: void 0 };
86
- }
87
- } else if (options.llmProvider || options.llmModel || options.llmBaseUrl || options.llmTasks) {
88
- if (config.releaseNotes === false) {
89
- warn("--llm-* flags ignored: release notes are disabled via --no-release-notes");
90
- } else {
91
- const existingRn = typeof config.releaseNotes === "object" ? config.releaseNotes : {};
92
- const existingLlm = existingRn.llm;
93
- const llm = {
94
- provider: existingLlm?.provider ?? "openai-compatible",
95
- model: existingLlm?.model ?? "",
96
- ...existingLlm ?? {}
97
- };
98
- if (options.llmProvider) llm.provider = options.llmProvider;
99
- if (options.llmModel) llm.model = options.llmModel;
100
- if (options.llmBaseUrl) llm.baseURL = options.llmBaseUrl;
101
- if (options.llmTasks) {
102
- const taskNames = options.llmTasks.split(",").map((t) => t.trim());
103
- llm.tasks = {
104
- enhance: taskNames.includes("enhance"),
105
- summarize: taskNames.includes("summarize"),
106
- categorize: taskNames.includes("categorize"),
107
- releaseNotes: taskNames.includes("release-notes") || taskNames.includes("releaseNotes")
108
- };
109
- }
110
- config.releaseNotes = {
111
- ...existingRn,
112
- llm
113
- };
114
- info(`LLM configured: ${llm.provider}${llm.model ? ` (${llm.model})` : ""}`);
115
- if (llm.baseURL) {
116
- info(`LLM base URL: ${llm.baseURL}`);
117
- }
118
- const taskList = Object.entries(llm.tasks || {}).filter(([, enabled]) => enabled).map(([name]) => name).join(", ");
119
- if (taskList) {
120
- info(`LLM tasks: ${taskList}`);
121
- }
122
- }
123
- }
124
- let inputJson;
125
- if (options.input) {
126
- inputJson = fs.readFileSync(options.input, "utf-8");
127
- } else {
128
- inputJson = await readStdin();
129
- }
130
- const input = parseVersionOutput(inputJson);
131
- if (options.target) {
132
- const before = input.packages.length;
133
- input.packages = input.packages.filter((p) => p.packageName === options.target);
134
- if (input.packages.length === 0) {
135
- info(`No changelog found for package "${options.target}" (had ${before} package(s))`);
136
- return;
137
- }
138
- info(`Filtered to package: ${options.target}`);
139
- }
140
- if (options.monorepo) {
141
- const monoMode = options.monorepo;
142
- config.monorepo = { ...config.monorepo, mode: monoMode };
143
- if (config.changelog !== false && !options.changelogMode) {
144
- config.changelog = { ...config.changelog ?? {}, mode: monoMode };
145
- }
146
- if (config.releaseNotes !== false && config.releaseNotes !== void 0 && !options.releaseNotesMode) {
147
- config.releaseNotes = { ...config.releaseNotes, mode: monoMode };
148
- }
149
- }
150
- await runPipeline(input, config, options.dryRun ?? false);
151
- if (options.dryRun) {
152
- info("Dry run complete - no files were written");
153
- } else {
154
- success("Changelog generation complete");
155
- }
156
- } catch (err) {
157
- handleError(err);
158
- }
159
- });
160
- cmd.command("auth <provider>").description("Configure API key for an LLM provider").option("--key <key>", "API key (omit to be prompted)").action(async (provider, options) => {
161
- let apiKey;
162
- if (options.key) {
163
- apiKey = options.key;
164
- } else {
165
- apiKey = await promptSecret(`Enter API key for ${provider}: `);
166
- }
167
- if (!apiKey.trim()) {
168
- error("API key cannot be empty");
169
- process.exit(EXIT_CODES.GENERAL_ERROR);
170
- }
171
- saveAuth(provider, apiKey.trim());
172
- success(`API key saved for ${provider}`);
173
- });
174
- cmd.command("providers").description("List available LLM providers").action(() => {
175
- info("Available LLM providers:");
176
- console.log(" openai - OpenAI (GPT models)");
177
- console.log(" anthropic - Anthropic (Claude models)");
178
- console.log(" ollama - Ollama (local models)");
179
- console.log(" openai-compatible - Any OpenAI-compatible endpoint");
180
- });
181
- return cmd;
182
- }
183
- function increaseVerbosity(_, previous) {
184
- return previous + 1;
185
- }
186
- function setVerbosity(level) {
187
- const levels = ["info", "debug", "trace"];
188
- setLogLevel(levels[Math.min(level, levels.length - 1)] ?? "info");
189
- }
190
- async function readStdin() {
191
- const chunks = [];
192
- for await (const chunk of process.stdin) {
193
- chunks.push(chunk);
194
- }
195
- return chunks.join("");
196
- }
197
- function promptSecret(prompt) {
198
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
199
- return new Promise((resolve) => {
200
- rl.question(prompt, (answer) => {
201
- rl.close();
202
- resolve(answer);
203
- });
204
- });
205
- }
206
- function handleError(err) {
207
- if (err instanceof NotesError) {
208
- err.logError();
209
- process.exit(getExitCode(err));
210
- }
211
- error(err instanceof Error ? err.message : String(err));
212
- process.exit(EXIT_CODES.GENERAL_ERROR);
213
- }
214
12
  var isMain = (() => {
215
13
  try {
216
14
  return process.argv[1] ? fs.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url) : false;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { createNotesCommand } from './command.ts';
1
2
  export { getDefaultConfig, loadAuth, loadConfig, saveAuth } from './core/config.ts';
2
3
  export { PipelineResult, createTemplateContext, processInput, runPipeline } from './core/pipeline.ts';
3
4
  export * from './core/types.ts';
package/dist/index.js CHANGED
@@ -5,18 +5,19 @@ import {
5
5
  LLMError,
6
6
  NotesError,
7
7
  TemplateError,
8
+ createNotesCommand,
8
9
  createTemplateContext,
9
10
  getDefaultConfig,
10
11
  getExitCode,
11
12
  loadAuth,
12
- loadConfig2 as loadConfig,
13
+ loadConfig,
13
14
  parseVersionOutput,
14
15
  parseVersionOutputFile,
15
16
  parseVersionOutputStdin,
16
17
  processInput,
17
18
  runPipeline,
18
19
  saveAuth
19
- } from "./chunk-FNIE2SHL.js";
20
+ } from "./chunk-Y4S5UWCL.js";
20
21
  import {
21
22
  aggregateToRoot,
22
23
  detectMonorepo,
@@ -74,6 +75,7 @@ export {
74
75
  NotesError,
75
76
  TemplateError,
76
77
  aggregateToRoot,
78
+ createNotesCommand,
77
79
  createTemplateContext,
78
80
  detectMonorepo,
79
81
  formatVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@releasekit/notes",
3
- "version": "0.7.10",
3
+ "version": "0.7.12",
4
4
  "description": "Release notes and changelog generation with LLM-powered enhancement and flexible templating",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -66,8 +66,8 @@
66
66
  "tsup": "^8.5.1",
67
67
  "typescript": "^5.9.3",
68
68
  "vitest": "^4.1.2",
69
- "@releasekit/config": "0.0.0",
70
- "@releasekit/core": "0.0.0"
69
+ "@releasekit/core": "0.0.0",
70
+ "@releasekit/config": "0.0.0"
71
71
  },
72
72
  "engines": {
73
73
  "node": ">=20"