tst-changelog 0.1.3 → 0.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.
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ interface AIConfig {
6
6
  }
7
7
  interface ChangelogConfig {
8
8
  file: string;
9
+ jsonFile?: string;
9
10
  format: 'keepachangelog' | 'simple';
10
11
  includeDate: boolean;
11
12
  includeTime?: boolean;
@@ -14,6 +15,17 @@ interface ChangelogConfig {
14
15
  repoUrl?: string;
15
16
  groupBy: 'type' | 'scope' | 'none';
16
17
  }
18
+ interface ChangelogJsonRelease {
19
+ version: string;
20
+ date: string;
21
+ time?: string;
22
+ author?: string;
23
+ commitHash?: string;
24
+ sections: Record<string, string[]>;
25
+ }
26
+ interface ChangelogJson {
27
+ releases: ChangelogJsonRelease[];
28
+ }
17
29
  interface ChangelogMetadata {
18
30
  author?: string;
19
31
  commitHash?: string;
@@ -146,6 +158,10 @@ declare class ChangelogService {
146
158
  formatEntry(generated: GeneratedChangelog, version?: string, metadata?: ChangelogMetadata): string;
147
159
  private sortSections;
148
160
  private formatSection;
161
+ /**
162
+ * Convert commit hashes in item to markdown links if repoUrl is configured
163
+ */
164
+ private formatItemWithLinks;
149
165
  /**
150
166
  * Update or create changelog with new entry
151
167
  */
@@ -163,6 +179,26 @@ declare class ChangelogService {
163
179
  * Get the file path
164
180
  */
165
181
  getFilePath(): string;
182
+ /**
183
+ * Get JSON file path if configured
184
+ */
185
+ getJsonFilePath(): string | null;
186
+ /**
187
+ * Read existing JSON changelog
188
+ */
189
+ readJson(): ChangelogJson | null;
190
+ /**
191
+ * Create a JSON release entry from generated changelog
192
+ */
193
+ createJsonRelease(generated: GeneratedChangelog, version?: string, metadata?: ChangelogMetadata): ChangelogJsonRelease;
194
+ /**
195
+ * Update JSON changelog with new release
196
+ */
197
+ updateJson(generated: GeneratedChangelog, version?: string, metadata?: ChangelogMetadata): ChangelogJson;
198
+ /**
199
+ * Write JSON changelog to file
200
+ */
201
+ writeJson(data: ChangelogJson): void;
166
202
  }
167
203
 
168
- export { type AIConfig, AIService, type CLIOptions, type ChangeType, type ChangelogConfig, type ChangelogEntry, type ChangelogMetadata, type ChangelogSection, ChangelogService, type CommitInfo, type Config, type GeneratedChangelog, type GitConfig, GitService, type InitResult, type StagedChanges, initConfig, loadConfig };
204
+ export { type AIConfig, AIService, type CLIOptions, type ChangeType, type ChangelogConfig, type ChangelogEntry, type ChangelogJson, type ChangelogJsonRelease, type ChangelogMetadata, type ChangelogSection, ChangelogService, type CommitInfo, type Config, type GeneratedChangelog, type GitConfig, GitService, type InitResult, type StagedChanges, initConfig, loadConfig };
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ Return ONLY a JSON object with the following structure:
19
19
  "sections": [
20
20
  {
21
21
  "type": "Added" | "Changed" | "Deprecated" | "Removed" | "Fixed" | "Security",
22
- "items": ["description 1", "description 2"]
22
+ "items": ["description (by Author, YYYY-MM-DD HH:MM, hash)", "description 2 (by Author, YYYY-MM-DD HH:MM, hash)"]
23
23
  }
24
24
  ]
25
25
  }
@@ -30,6 +30,8 @@ Guidelines:
30
30
  - Group related changes together
31
31
  - Focus on user-facing changes
32
32
  - Ignore merge commits and trivial changes
33
+ - IMPORTANT: Each item MUST include author, datetime, and commit hash from the input in the format: "description (by Author, YYYY-MM-DD HH:MM, hash)"
34
+ - If multiple commits contribute to the same change, list them: "description (by Author1, datetime1, hash1; Author2, datetime2, hash2)"
33
35
  - Parse conventional commits (feat:, fix:, etc.) into appropriate sections:
34
36
  - feat: -> Added
35
37
  - fix: -> Fixed
@@ -42,11 +44,12 @@ var DEFAULT_CONFIG = {
42
44
  ai: {
43
45
  provider: "openrouter",
44
46
  model: "anthropic/claude-sonnet-4.5",
45
- token: "",
47
+ token: "${OPENROUTER_API_KEY}",
46
48
  prompt: DEFAULT_PROMPT
47
49
  },
48
50
  changelog: {
49
51
  file: "CHANGELOG.md",
52
+ jsonFile: "changelog.json",
50
53
  format: "keepachangelog",
51
54
  includeDate: true,
52
55
  includeTime: true,
@@ -110,7 +113,6 @@ function loadConfig(configPath) {
110
113
  }
111
114
  }
112
115
  if (!filePath) {
113
- console.warn("No config file found, using defaults");
114
116
  return resolveConfigEnvVars(DEFAULT_CONFIG);
115
117
  }
116
118
  const content = readFileSync(filePath, "utf-8");
@@ -132,6 +134,7 @@ function initConfig(fileName = "tst-changelog.yaml") {
132
134
  },
133
135
  changelog: {
134
136
  file: "CHANGELOG.md",
137
+ jsonFile: "changelog.json",
135
138
  format: "keepachangelog",
136
139
  includeDate: true,
137
140
  includeTime: true,
@@ -159,20 +162,21 @@ function configureHusky() {
159
162
  return { huskyConfigured: false };
160
163
  }
161
164
  const preCommitPath = resolve(huskyDir, "pre-commit");
162
- const command = "npx tst-changelog";
165
+ const commands = `npx tst-changelog generate
166
+ git add CHANGELOG.md changelog.json 2>/dev/null || true`;
163
167
  if (existsSync(preCommitPath)) {
164
168
  const content = readFileSync(preCommitPath, "utf-8");
165
169
  if (content.includes("tst-changelog")) {
166
170
  return { huskyConfigured: false };
167
171
  }
168
172
  appendFileSync(preCommitPath, `
169
- ${command}
173
+ ${commands}
170
174
  `);
171
175
  } else {
172
176
  writeFileSync(preCommitPath, `#!/usr/bin/env sh
173
177
  . "$(dirname -- "$0")/_/husky.sh"
174
178
 
175
- ${command}
179
+ ${commands}
176
180
  `, { mode: 493 });
177
181
  }
178
182
  return { huskyConfigured: true, huskyHook: preCommitPath };
@@ -328,8 +332,12 @@ var AIService = class {
328
332
  const parts = [];
329
333
  if (commits.length > 0) {
330
334
  parts.push("## Recent Commits:");
331
- for (const commit of commits.slice(0, 20)) {
332
- parts.push(`- ${commit.message}`);
335
+ for (const commit of commits) {
336
+ const shortHash = commit.hash.slice(0, 7);
337
+ const [date, timePart] = commit.date.split("T");
338
+ const time = timePart?.slice(0, 5) ?? "";
339
+ const datetime = time ? `${date} ${time}` : date;
340
+ parts.push(`- [${shortHash}] (${commit.author}, ${datetime}) ${commit.message}`);
333
341
  if (commit.body) {
334
342
  parts.push(` ${commit.body}`);
335
343
  }
@@ -366,7 +374,7 @@ var AIService = class {
366
374
  { role: "user", content: prompt }
367
375
  ],
368
376
  temperature: 0.3,
369
- max_tokens: 1e3
377
+ max_tokens: 4e3
370
378
  })
371
379
  });
372
380
  if (!response.ok) {
@@ -479,11 +487,21 @@ ${sectionLines}`;
479
487
  });
480
488
  }
481
489
  formatSection(section) {
482
- const items = section.items.map((item) => `- ${item}`).join("\n");
490
+ const items = section.items.map((item) => `- ${this.formatItemWithLinks(item)}`).join("\n");
483
491
  return `### ${section.type}
484
492
 
485
493
  ${items}`;
486
494
  }
495
+ /**
496
+ * Convert commit hashes in item to markdown links if repoUrl is configured
497
+ */
498
+ formatItemWithLinks(item) {
499
+ if (!this.config.repoUrl) return item;
500
+ const repoUrl = this.config.repoUrl.replace(/\/$/, "");
501
+ return item.replace(/\b([a-f0-9]{7})\b/g, (match) => {
502
+ return `[${match}](${repoUrl}/commit/${match})`;
503
+ });
504
+ }
487
505
  /**
488
506
  * Update or create changelog with new entry
489
507
  */
@@ -599,6 +617,91 @@ ${sectionLines}`;
599
617
  getFilePath() {
600
618
  return this.filePath;
601
619
  }
620
+ /**
621
+ * Get JSON file path if configured
622
+ */
623
+ getJsonFilePath() {
624
+ if (!this.config.jsonFile) return null;
625
+ return resolve2(process.cwd(), this.config.jsonFile);
626
+ }
627
+ /**
628
+ * Read existing JSON changelog
629
+ */
630
+ readJson() {
631
+ const jsonPath = this.getJsonFilePath();
632
+ if (!jsonPath || !existsSync2(jsonPath)) {
633
+ return null;
634
+ }
635
+ try {
636
+ const content = readFileSync2(jsonPath, "utf-8");
637
+ return JSON.parse(content);
638
+ } catch {
639
+ return null;
640
+ }
641
+ }
642
+ /**
643
+ * Create a JSON release entry from generated changelog
644
+ */
645
+ createJsonRelease(generated, version, metadata) {
646
+ const now = metadata?.timestamp ?? /* @__PURE__ */ new Date();
647
+ const sections = {};
648
+ for (const section of generated.sections) {
649
+ sections[section.type] = section.items;
650
+ }
651
+ const release = {
652
+ version: version ?? "Unreleased",
653
+ date: now.toISOString().split("T")[0],
654
+ sections
655
+ };
656
+ if (this.config.includeTime) {
657
+ release.time = now.toTimeString().split(" ")[0].slice(0, 5);
658
+ }
659
+ if (this.config.includeAuthor && metadata?.author) {
660
+ release.author = metadata.author;
661
+ }
662
+ if (this.config.includeCommitLink && metadata?.commitHash) {
663
+ release.commitHash = metadata.commitHash;
664
+ }
665
+ return release;
666
+ }
667
+ /**
668
+ * Update JSON changelog with new release
669
+ */
670
+ updateJson(generated, version, metadata) {
671
+ const newRelease = this.createJsonRelease(generated, version, metadata);
672
+ const existing = this.readJson();
673
+ if (!existing) {
674
+ return { releases: [newRelease] };
675
+ }
676
+ const unreleasedIndex = existing.releases.findIndex((r) => r.version === "Unreleased");
677
+ if (unreleasedIndex >= 0 && newRelease.version === "Unreleased") {
678
+ const existingRelease = existing.releases[unreleasedIndex];
679
+ for (const [type, items] of Object.entries(newRelease.sections)) {
680
+ const existingItems = existingRelease.sections[type] ?? [];
681
+ for (const item of items) {
682
+ if (!existingItems.includes(item)) {
683
+ existingItems.push(item);
684
+ }
685
+ }
686
+ existingRelease.sections[type] = existingItems;
687
+ }
688
+ existingRelease.date = newRelease.date;
689
+ if (newRelease.time) existingRelease.time = newRelease.time;
690
+ if (newRelease.author) existingRelease.author = newRelease.author;
691
+ if (newRelease.commitHash) existingRelease.commitHash = newRelease.commitHash;
692
+ } else {
693
+ existing.releases.unshift(newRelease);
694
+ }
695
+ return existing;
696
+ }
697
+ /**
698
+ * Write JSON changelog to file
699
+ */
700
+ writeJson(data) {
701
+ const jsonPath = this.getJsonFilePath();
702
+ if (!jsonPath) return;
703
+ writeFileSync2(jsonPath, JSON.stringify(data, null, 2), "utf-8");
704
+ }
602
705
  };
603
706
 
604
707
  // src/index.ts
@@ -680,32 +783,48 @@ async function run(options) {
680
783
  }
681
784
  }
682
785
  }
683
- const [author, commitHash] = await Promise.all([
684
- git.getCurrentUser(),
685
- git.getHeadCommit()
686
- ]);
687
- const metadata = {
688
- author: author || void 0,
689
- commitHash: commitHash || void 0,
690
- timestamp: /* @__PURE__ */ new Date()
691
- };
786
+ let metadata = { timestamp: /* @__PURE__ */ new Date() };
787
+ if (!options.fromCommits) {
788
+ const [author, commitHash] = await Promise.all([
789
+ git.getCurrentUser(),
790
+ git.getHeadCommit()
791
+ ]);
792
+ metadata = {
793
+ author: author || void 0,
794
+ commitHash: commitHash || void 0,
795
+ timestamp: /* @__PURE__ */ new Date()
796
+ };
797
+ }
692
798
  if (verbose) {
693
799
  console.log("Metadata:", metadata);
694
800
  }
695
801
  const newContent = changelog.update(generated, void 0, metadata);
802
+ const jsonData = changelog.updateJson(generated, void 0, metadata);
696
803
  if (options.dryRun) {
697
804
  console.log("\n--- DRY RUN ---");
698
805
  console.log("Would update changelog at:", changelog.getFilePath());
699
- console.log("\n--- New entry ---");
806
+ if (changelog.getJsonFilePath()) {
807
+ console.log("Would update JSON at:", changelog.getJsonFilePath());
808
+ }
809
+ console.log("\n--- New MD entry ---");
700
810
  console.log(changelog.formatEntry(generated, void 0, metadata));
811
+ console.log("\n--- New JSON entry ---");
812
+ console.log(JSON.stringify(jsonData.releases[0], null, 2));
701
813
  console.log("--- End ---\n");
702
814
  process.exit(0);
703
815
  }
704
816
  changelog.write(newContent);
705
817
  console.log(`Updated: ${changelog.getFilePath()}`);
818
+ if (changelog.getJsonFilePath()) {
819
+ changelog.writeJson(jsonData);
820
+ console.log(`Updated: ${changelog.getJsonFilePath()}`);
821
+ }
706
822
  if (!options.fromCommits) {
707
823
  await git.stageFile(config.changelog.file);
708
- console.log("Staged changelog file");
824
+ if (config.changelog.jsonFile) {
825
+ await git.stageFile(config.changelog.jsonFile);
826
+ }
827
+ console.log("Staged changelog files");
709
828
  }
710
829
  console.log("Done!");
711
830
  } catch (error) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/git.ts","../src/ai.ts","../src/changelog.ts"],"sourcesContent":["import 'dotenv/config';\nimport { Command } from 'commander';\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, resolve } from 'node:path';\nimport { loadConfig, initConfig } from './config.js';\nimport { GitService } from './git.js';\nimport { AIService } from './ai.js';\nimport { ChangelogService } from './changelog.js';\nimport type { CLIOptions, ChangelogMetadata } from './types.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf-8'));\n\nconst program = new Command();\n\nprogram\n .name('tst-changelog')\n .description('AI-powered changelog generator using OpenRouter')\n .version(pkg.version);\n\nprogram\n .command('init')\n .description('Create a default configuration file')\n .option('-f, --file <name>', 'Config file name', 'tst-changelog.yaml')\n .action((options: { file: string }) => {\n try {\n const result = initConfig(options.file);\n console.log(`Created config file: ${result.configPath}`);\n\n if (result.huskyConfigured) {\n console.log(`Configured husky hook: ${result.huskyHook}`);\n }\n\n console.log('\\nNext steps:');\n console.log('1. Set the OPENROUTER_API_KEY environment variable');\n console.log('2. Adjust settings in the config file as needed');\n if (!result.huskyConfigured) {\n console.log('3. Run: npx tst-changelog generate');\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram\n .command('generate', { isDefault: true })\n .description('Generate changelog from staged changes or commit history')\n .option('-c, --config <path>', 'Path to config file')\n .option('-d, --dry-run', 'Preview without modifying files')\n .option('-v, --verbose', 'Enable verbose output')\n .option('--from-commits <count>', 'Generate from last N commits instead of staged changes', parseInt)\n .action(run);\n\nasync function run(options: CLIOptions): Promise<void> {\n const verbose = options.verbose ?? false;\n\n try {\n // Load configuration\n if (verbose) console.log('Loading configuration...');\n const config = loadConfig(options.config);\n if (verbose) console.log('Config loaded:', JSON.stringify(config, null, 2));\n\n // Initialize services\n const git = new GitService(config.git);\n const ai = new AIService(config.ai);\n const changelog = new ChangelogService(config.changelog);\n\n // Check if we're in a git repo\n if (!(await git.isGitRepo())) {\n console.error('Error: Not a git repository');\n process.exit(1);\n }\n\n let commits;\n let staged;\n\n if (options.fromCommits) {\n // Generate from commit history\n if (verbose) console.log(`Getting last ${options.fromCommits} commits...`);\n commits = await git.getRecentCommits(options.fromCommits);\n\n if (commits.length === 0) {\n console.log('No commits found.');\n process.exit(0);\n }\n\n if (verbose) console.log(`Found ${commits.length} commits`);\n\n // Empty staged for history mode\n staged = { files: [], diff: '' };\n } else {\n // Get staged changes (default behavior)\n if (verbose) console.log('Getting staged changes...');\n staged = await git.getStagedChanges();\n\n if (staged.files.length === 0) {\n console.log('No staged changes found. Stage some changes first.');\n process.exit(0);\n }\n\n if (verbose) {\n console.log(`Staged files: ${staged.files.join(', ')}`);\n console.log(`Diff length: ${staged.diff.length} chars`);\n }\n\n // Get recent commits for context\n if (verbose) console.log('Getting recent commits...');\n commits = await git.getUnmergedCommits();\n if (verbose) console.log(`Found ${commits.length} unmerged commits`);\n }\n\n // Generate changelog using AI\n console.log('Generating changelog with AI...');\n const generated = await ai.generateChangelog(commits, staged);\n\n if (generated.sections.length === 0) {\n console.log('No changelog entries generated.');\n if (verbose) console.log('Raw AI response:', generated.raw);\n process.exit(0);\n }\n\n if (verbose) {\n console.log('Generated sections:');\n for (const section of generated.sections) {\n console.log(` ${section.type}:`);\n for (const item of section.items) {\n console.log(` - ${item}`);\n }\n }\n }\n\n // Collect metadata for changelog entry\n const [author, commitHash] = await Promise.all([\n git.getCurrentUser(),\n git.getHeadCommit(),\n ]);\n\n const metadata: ChangelogMetadata = {\n author: author || undefined,\n commitHash: commitHash || undefined,\n timestamp: new Date(),\n };\n\n if (verbose) {\n console.log('Metadata:', metadata);\n }\n\n // Update changelog\n const newContent = changelog.update(generated, undefined, metadata);\n\n if (options.dryRun) {\n console.log('\\n--- DRY RUN ---');\n console.log('Would update changelog at:', changelog.getFilePath());\n console.log('\\n--- New entry ---');\n console.log(changelog.formatEntry(generated, undefined, metadata));\n console.log('--- End ---\\n');\n process.exit(0);\n }\n\n // Write changelog\n changelog.write(newContent);\n console.log(`Updated: ${changelog.getFilePath()}`);\n\n // Only auto-stage when using staged changes mode (not --from-commits)\n if (!options.fromCommits) {\n await git.stageFile(config.changelog.file);\n console.log('Staged changelog file');\n }\n\n console.log('Done!');\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n if (verbose && error instanceof Error) {\n console.error(error.stack);\n }\n process.exit(1);\n }\n}\n\nprogram.parse();\n\n// Export for programmatic usage\nexport { loadConfig, initConfig, type InitResult } from './config.js';\nexport { GitService } from './git.js';\nexport { AIService } from './ai.js';\nexport { ChangelogService } from './changelog.js';\nexport * from './types.js';\n","import { readFileSync, existsSync, writeFileSync, appendFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parse, stringify } from 'yaml';\nimport type { Config } from './types.js';\n\nconst DEFAULT_PROMPT = `You are a changelog generator. Your task is to analyze git commits and staged changes to generate changelog entries following the Keep a Changelog format.\n\nOutput Format:\nReturn ONLY a JSON object with the following structure:\n{\n \"sections\": [\n {\n \"type\": \"Added\" | \"Changed\" | \"Deprecated\" | \"Removed\" | \"Fixed\" | \"Security\",\n \"items\": [\"description 1\", \"description 2\"]\n }\n ]\n}\n\nGuidelines:\n- Use present tense (e.g., \"Add feature\" not \"Added feature\")\n- Be concise but descriptive\n- Group related changes together\n- Focus on user-facing changes\n- Ignore merge commits and trivial changes\n- Parse conventional commits (feat:, fix:, etc.) into appropriate sections:\n - feat: -> Added\n - fix: -> Fixed\n - docs:, style:, refactor:, perf:, test:, chore: -> Changed\n - BREAKING CHANGE: -> Changed (mention breaking)\n - deprecate: -> Deprecated\n - remove: -> Removed\n - security: -> Security`;\n\nconst DEFAULT_CONFIG: Config = {\n ai: {\n provider: 'openrouter',\n model: 'anthropic/claude-sonnet-4.5',\n token: '',\n prompt: DEFAULT_PROMPT,\n },\n changelog: {\n file: 'CHANGELOG.md',\n format: 'keepachangelog',\n includeDate: true,\n includeTime: true,\n includeAuthor: true,\n includeCommitLink: true,\n repoUrl: '',\n groupBy: 'type',\n },\n git: {\n baseBranch: 'main',\n analyzeDepth: 50,\n },\n};\n\n/**\n * Resolve environment variables in config values\n * Supports ${ENV_VAR} syntax\n */\nfunction resolveEnvVars(value: string): string {\n return value.replace(/\\$\\{([^}]+)\\}/g, (_, envVar) => {\n const envValue = process.env[envVar];\n if (!envValue) {\n throw new Error(`Environment variable ${envVar} is not set`);\n }\n return envValue;\n });\n}\n\n/**\n * Recursively resolve env vars in object\n */\nfunction resolveConfigEnvVars<T>(obj: T): T {\n if (typeof obj === 'string') {\n return resolveEnvVars(obj) as T;\n }\n if (Array.isArray(obj)) {\n return obj.map(resolveConfigEnvVars) as T;\n }\n if (obj && typeof obj === 'object') {\n const resolved: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n resolved[key] = resolveConfigEnvVars(value);\n }\n return resolved as T;\n }\n return obj;\n}\n\n/**\n * Deep merge two objects\n */\nfunction deepMerge(target: Config, source: Partial<Config>): Config {\n return {\n ai: { ...target.ai, ...source.ai },\n changelog: { ...target.changelog, ...source.changelog },\n git: { ...target.git, ...source.git },\n };\n}\n\n/**\n * Load configuration from yaml file\n */\nexport function loadConfig(configPath?: string): Config {\n const defaultPaths = ['tst-changelog.yaml', 'tst-changelog.yml', '.tst-changelog.yaml'];\n\n let filePath: string | undefined;\n\n if (configPath) {\n filePath = resolve(process.cwd(), configPath);\n if (!existsSync(filePath)) {\n throw new Error(`Config file not found: ${filePath}`);\n }\n } else {\n for (const p of defaultPaths) {\n const fullPath = resolve(process.cwd(), p);\n if (existsSync(fullPath)) {\n filePath = fullPath;\n break;\n }\n }\n }\n\n if (!filePath) {\n console.warn('No config file found, using defaults');\n return resolveConfigEnvVars(DEFAULT_CONFIG);\n }\n\n const content = readFileSync(filePath, 'utf-8');\n const parsed = parse(content) as Partial<Config>;\n\n const merged = deepMerge(DEFAULT_CONFIG, parsed);\n return resolveConfigEnvVars(merged);\n}\n\nexport interface InitResult {\n configPath: string;\n huskyConfigured: boolean;\n huskyHook?: string;\n}\n\n/**\n * Initialize a new config file with defaults\n */\nexport function initConfig(fileName = 'tst-changelog.yaml'): InitResult {\n const filePath = resolve(process.cwd(), fileName);\n\n if (existsSync(filePath)) {\n throw new Error(`Config file already exists: ${filePath}`);\n }\n\n const configTemplate: Config = {\n ai: {\n provider: 'openrouter',\n model: 'anthropic/claude-sonnet-4.5',\n token: '${OPENROUTER_API_KEY}',\n prompt: DEFAULT_PROMPT,\n },\n changelog: {\n file: 'CHANGELOG.md',\n format: 'keepachangelog',\n includeDate: true,\n includeTime: true,\n includeAuthor: true,\n includeCommitLink: true,\n repoUrl: '',\n groupBy: 'type',\n },\n git: {\n baseBranch: 'main',\n analyzeDepth: 50,\n },\n };\n\n const content = stringify(configTemplate, { lineWidth: 0 });\n writeFileSync(filePath, content, 'utf-8');\n\n // Check for husky and configure hook\n const huskyResult = configureHusky();\n\n return {\n configPath: filePath,\n ...huskyResult,\n };\n}\n\n/**\n * Configure husky pre-commit hook if husky is present\n */\nfunction configureHusky(): { huskyConfigured: boolean; huskyHook?: string } {\n const huskyDir = resolve(process.cwd(), '.husky');\n\n if (!existsSync(huskyDir)) {\n return { huskyConfigured: false };\n }\n\n const preCommitPath = resolve(huskyDir, 'pre-commit');\n const command = 'npx tst-changelog';\n\n // Check if hook already exists\n if (existsSync(preCommitPath)) {\n const content = readFileSync(preCommitPath, 'utf-8');\n if (content.includes('tst-changelog')) {\n return { huskyConfigured: false }; // Already configured\n }\n // Append to existing hook\n appendFileSync(preCommitPath, `\\n${command}\\n`);\n } else {\n // Create new hook\n writeFileSync(preCommitPath, `#!/usr/bin/env sh\\n. \"$(dirname -- \"$0\")/_/husky.sh\"\\n\\n${command}\\n`, { mode: 0o755 });\n }\n\n return { huskyConfigured: true, huskyHook: preCommitPath };\n}\n","import simpleGit, { SimpleGit } from 'simple-git';\nimport type { CommitInfo, StagedChanges, GitConfig } from './types.js';\n\nexport class GitService {\n private git: SimpleGit;\n private config: GitConfig;\n\n constructor(config: GitConfig, cwd?: string) {\n this.git = simpleGit(cwd);\n this.config = config;\n }\n\n /**\n * Get list of staged files\n */\n async getStagedFiles(): Promise<string[]> {\n const status = await this.git.status();\n return status.staged;\n }\n\n /**\n * Get diff of staged changes\n */\n async getStagedDiff(): Promise<string> {\n const diff = await this.git.diff(['--cached']);\n return diff;\n }\n\n /**\n * Get staged changes (files + diff)\n */\n async getStagedChanges(): Promise<StagedChanges> {\n const [files, diff] = await Promise.all([\n this.getStagedFiles(),\n this.getStagedDiff(),\n ]);\n return { files, diff };\n }\n\n /**\n * Get recent commits from the base branch\n */\n async getRecentCommits(count?: number): Promise<CommitInfo[]> {\n const limit = count ?? this.config.analyzeDepth;\n const log = await this.git.log({\n maxCount: limit,\n format: {\n hash: '%H',\n message: '%s',\n author: '%an',\n date: '%aI',\n body: '%b',\n },\n });\n\n return log.all.map((commit) => ({\n hash: commit.hash,\n message: commit.message,\n author: commit.author,\n date: commit.date,\n body: commit.body?.trim() || undefined,\n }));\n }\n\n /**\n * Get commits not yet in base branch (for feature branches)\n */\n async getUnmergedCommits(): Promise<CommitInfo[]> {\n try {\n const log = await this.git.log({\n from: this.config.baseBranch,\n to: 'HEAD',\n format: {\n hash: '%H',\n message: '%s',\n author: '%an',\n date: '%aI',\n body: '%b',\n },\n });\n\n return log.all.map((commit) => ({\n hash: commit.hash,\n message: commit.message,\n author: commit.author,\n date: commit.date,\n body: commit.body?.trim() || undefined,\n }));\n } catch {\n // If base branch doesn't exist, return empty\n return [];\n }\n }\n\n /**\n * Get current branch name\n */\n async getCurrentBranch(): Promise<string> {\n const branch = await this.git.revparse(['--abbrev-ref', 'HEAD']);\n return branch.trim();\n }\n\n /**\n * Stage a file\n */\n async stageFile(filePath: string): Promise<void> {\n await this.git.add(filePath);\n }\n\n /**\n * Check if we're in a git repository\n */\n async isGitRepo(): Promise<boolean> {\n try {\n await this.git.revparse(['--git-dir']);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get current git user name\n */\n async getCurrentUser(): Promise<string> {\n try {\n const name = await this.git.getConfig('user.name');\n return name.value ?? '';\n } catch {\n return '';\n }\n }\n\n /**\n * Get HEAD commit hash\n */\n async getHeadCommit(): Promise<string> {\n try {\n const hash = await this.git.revparse(['HEAD']);\n return hash.trim();\n } catch {\n return '';\n }\n }\n}\n","import type { AIConfig, CommitInfo, StagedChanges, GeneratedChangelog, ChangelogSection, ChangeType } from './types.js';\n\nconst OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';\n\ninterface OpenRouterResponse {\n choices: Array<{\n message: {\n content: string;\n };\n }>;\n}\n\nexport class AIService {\n private config: AIConfig;\n\n constructor(config: AIConfig) {\n this.config = config;\n }\n\n /**\n * Generate changelog entries from commits and staged changes\n */\n async generateChangelog(\n commits: CommitInfo[],\n staged: StagedChanges\n ): Promise<GeneratedChangelog> {\n const userPrompt = this.buildPrompt(commits, staged);\n const response = await this.callOpenRouter(userPrompt);\n return this.parseResponse(response);\n }\n\n private buildPrompt(commits: CommitInfo[], staged: StagedChanges): string {\n const parts: string[] = [];\n\n if (commits.length > 0) {\n parts.push('## Recent Commits:');\n for (const commit of commits.slice(0, 20)) {\n parts.push(`- ${commit.message}`);\n if (commit.body) {\n parts.push(` ${commit.body}`);\n }\n }\n }\n\n if (staged.files.length > 0) {\n parts.push('\\n## Staged Files:');\n parts.push(staged.files.join('\\n'));\n }\n\n if (staged.diff) {\n // Limit diff size to avoid token limits\n const maxDiffLength = 4000;\n const truncatedDiff = staged.diff.length > maxDiffLength\n ? staged.diff.slice(0, maxDiffLength) + '\\n... (truncated)'\n : staged.diff;\n parts.push('\\n## Staged Diff:');\n parts.push('```diff');\n parts.push(truncatedDiff);\n parts.push('```');\n }\n\n parts.push('\\nGenerate changelog entries for these changes.');\n\n return parts.join('\\n');\n }\n\n private async callOpenRouter(prompt: string): Promise<string> {\n const response = await fetch(OPENROUTER_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.token}`,\n 'HTTP-Referer': 'https://github.com/testudosrl/tst-libs',\n 'X-Title': 'tst-changelog',\n },\n body: JSON.stringify({\n model: this.config.model,\n messages: [\n { role: 'system', content: this.config.prompt ?? '' },\n { role: 'user', content: prompt },\n ],\n temperature: 0.3,\n max_tokens: 1000,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`OpenRouter API error: ${response.status} - ${error}`);\n }\n\n const data = (await response.json()) as OpenRouterResponse;\n return data.choices[0]?.message?.content ?? '';\n }\n\n private parseResponse(response: string): GeneratedChangelog {\n try {\n // Extract JSON from response (handle markdown code blocks or raw JSON)\n let jsonStr = response;\n\n // Try markdown code block first\n const codeBlockMatch = response.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n if (codeBlockMatch) {\n jsonStr = codeBlockMatch[1];\n } else {\n // Try to find JSON object in the response\n const jsonObjectMatch = response.match(/\\{[\\s\\S]*\"sections\"[\\s\\S]*\\}/);\n if (jsonObjectMatch) {\n jsonStr = jsonObjectMatch[0];\n }\n }\n\n const parsed = JSON.parse(jsonStr.trim()) as { sections: ChangelogSection[] };\n\n // Validate and normalize sections\n const validTypes: ChangeType[] = ['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security'];\n const sections = parsed.sections\n .filter((s) => validTypes.includes(s.type))\n .map((s) => ({\n type: s.type,\n items: Array.isArray(s.items) ? s.items.filter((i) => typeof i === 'string') : [],\n }))\n .filter((s) => s.items.length > 0);\n\n return {\n raw: response,\n sections,\n };\n } catch (error) {\n console.error('Failed to parse AI response:', error);\n console.error('Raw response:', response);\n return {\n raw: response,\n sections: [],\n };\n }\n }\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type { ChangelogConfig, ChangelogSection, GeneratedChangelog, ChangelogMetadata } from './types.js';\n\nconst KEEPACHANGELOG_HEADER = `# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n`;\n\nconst SECTION_ORDER = ['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security'] as const;\n\nexport class ChangelogService {\n private config: ChangelogConfig;\n private filePath: string;\n\n constructor(config: ChangelogConfig, cwd?: string) {\n this.config = config;\n this.filePath = resolve(cwd ?? process.cwd(), config.file);\n }\n\n /**\n * Read existing changelog content\n */\n read(): string | null {\n if (!existsSync(this.filePath)) {\n return null;\n }\n return readFileSync(this.filePath, 'utf-8');\n }\n\n /**\n * Generate new changelog entry\n */\n formatEntry(generated: GeneratedChangelog, version?: string, metadata?: ChangelogMetadata): string {\n const versionStr = version ?? 'Unreleased';\n const headerParts: string[] = [`## [${versionStr}]`];\n\n // Date and time\n if (this.config.includeDate) {\n const now = metadata?.timestamp ?? new Date();\n let dateStr = now.toISOString().split('T')[0];\n if (this.config.includeTime) {\n const timeStr = now.toTimeString().split(' ')[0].slice(0, 5); // HH:MM\n dateStr += ` ${timeStr}`;\n }\n headerParts.push(`- ${dateStr}`);\n }\n\n // Author\n if (this.config.includeAuthor && metadata?.author) {\n headerParts.push(`by ${metadata.author}`);\n }\n\n // Commit link\n if (this.config.includeCommitLink && metadata?.commitHash) {\n const shortHash = metadata.commitHash.slice(0, 7);\n if (this.config.repoUrl) {\n const commitUrl = `${this.config.repoUrl.replace(/\\/$/, '')}/commit/${metadata.commitHash}`;\n headerParts.push(`([${shortHash}](${commitUrl}))`);\n } else {\n headerParts.push(`(${shortHash})`);\n }\n }\n\n const headerLine = headerParts.join(' ');\n\n const sections = this.sortSections(generated.sections);\n const sectionLines = sections\n .map((section) => this.formatSection(section))\n .join('\\n\\n');\n\n return `${headerLine}\\n\\n${sectionLines}`;\n }\n\n private sortSections(sections: ChangelogSection[]): ChangelogSection[] {\n return [...sections].sort((a, b) => {\n const indexA = SECTION_ORDER.indexOf(a.type as typeof SECTION_ORDER[number]);\n const indexB = SECTION_ORDER.indexOf(b.type as typeof SECTION_ORDER[number]);\n return indexA - indexB;\n });\n }\n\n private formatSection(section: ChangelogSection): string {\n const items = section.items.map((item) => `- ${item}`).join('\\n');\n return `### ${section.type}\\n\\n${items}`;\n }\n\n /**\n * Update or create changelog with new entry\n */\n update(generated: GeneratedChangelog, version?: string, metadata?: ChangelogMetadata): string {\n const existing = this.read();\n\n if (!existing) {\n // Create new changelog\n const newEntry = this.formatEntry(generated, version, metadata);\n return KEEPACHANGELOG_HEADER + newEntry + '\\n';\n }\n\n // Find where to insert new entry (after header, before first version)\n const lines = existing.split('\\n');\n let insertIndex = -1;\n\n // Look for ## [Unreleased] or first ## [ section\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.startsWith('## [Unreleased]')) {\n // Merge with existing unreleased section\n const endIndex = this.findSectionEnd(lines, i + 1);\n const existingSectionLines = lines.slice(i, endIndex);\n const mergedSection = this.mergeUnreleasedSection(existingSectionLines, generated, metadata);\n const before = lines.slice(0, i).join('\\n');\n const after = lines.slice(endIndex).join('\\n');\n return before + (before.endsWith('\\n') ? '' : '\\n') + mergedSection + '\\n' + after;\n }\n if (line.startsWith('## [') && insertIndex === -1) {\n insertIndex = i;\n break;\n }\n }\n\n const newEntry = this.formatEntry(generated, version, metadata);\n\n if (insertIndex === -1) {\n // No version sections found, append after header\n return existing.trimEnd() + '\\n\\n' + newEntry + '\\n';\n }\n\n // Insert before first version\n const before = lines.slice(0, insertIndex).join('\\n');\n const after = lines.slice(insertIndex).join('\\n');\n return before + (before.endsWith('\\n') ? '' : '\\n') + newEntry + '\\n\\n' + after;\n }\n\n /**\n * Merge new entries into existing Unreleased section\n */\n private mergeUnreleasedSection(\n existingLines: string[],\n generated: GeneratedChangelog,\n metadata?: ChangelogMetadata\n ): string {\n // Parse existing sections\n const existingSections: Map<string, string[]> = new Map();\n let currentSection: string | null = null;\n\n for (const line of existingLines) {\n if (line.startsWith('### ')) {\n currentSection = line.slice(4).trim();\n if (!existingSections.has(currentSection)) {\n existingSections.set(currentSection, []);\n }\n } else if (currentSection && line.startsWith('- ')) {\n existingSections.get(currentSection)!.push(line.slice(2));\n }\n }\n\n // Merge new sections\n for (const section of generated.sections) {\n const existing = existingSections.get(section.type) ?? [];\n // Add new items that don't already exist\n for (const item of section.items) {\n if (!existing.includes(item)) {\n existing.push(item);\n }\n }\n existingSections.set(section.type, existing);\n }\n\n // Build merged entry\n const versionStr = 'Unreleased';\n const headerParts: string[] = [`## [${versionStr}]`];\n\n if (this.config.includeDate) {\n const now = metadata?.timestamp ?? new Date();\n let dateStr = now.toISOString().split('T')[0];\n if (this.config.includeTime) {\n const timeStr = now.toTimeString().split(' ')[0].slice(0, 5);\n dateStr += ` ${timeStr}`;\n }\n headerParts.push(`- ${dateStr}`);\n }\n\n if (this.config.includeAuthor && metadata?.author) {\n headerParts.push(`by ${metadata.author}`);\n }\n\n if (this.config.includeCommitLink && metadata?.commitHash) {\n const shortHash = metadata.commitHash.slice(0, 7);\n if (this.config.repoUrl) {\n const commitUrl = `${this.config.repoUrl.replace(/\\/$/, '')}/commit/${metadata.commitHash}`;\n headerParts.push(`([${shortHash}](${commitUrl}))`);\n } else {\n headerParts.push(`(${shortHash})`);\n }\n }\n\n const headerLine = headerParts.join(' ');\n\n // Sort and format sections\n const sortedSections: ChangelogSection[] = [];\n for (const type of SECTION_ORDER) {\n const items = existingSections.get(type);\n if (items && items.length > 0) {\n sortedSections.push({ type, items });\n }\n }\n\n const sectionLines = sortedSections\n .map((section) => this.formatSection(section))\n .join('\\n\\n');\n\n return `${headerLine}\\n\\n${sectionLines}`;\n }\n\n private findSectionEnd(lines: string[], startIndex: number): number {\n for (let i = startIndex; i < lines.length; i++) {\n if (lines[i].startsWith('## [')) {\n return i;\n }\n }\n return lines.length;\n }\n\n /**\n * Write changelog to file\n */\n write(content: string): void {\n writeFileSync(this.filePath, content, 'utf-8');\n }\n\n /**\n * Get the file path\n */\n getFilePath(): string {\n return this.filePath;\n }\n}\n"],"mappings":"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","names":["readFileSync","resolve","readFileSync","writeFileSync","existsSync","resolve","newEntry","before","after","readFileSync","resolve"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/git.ts","../src/ai.ts","../src/changelog.ts"],"sourcesContent":["import 'dotenv/config';\nimport { Command } from 'commander';\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, resolve } from 'node:path';\nimport { loadConfig, initConfig } from './config.js';\nimport { GitService } from './git.js';\nimport { AIService } from './ai.js';\nimport { ChangelogService } from './changelog.js';\nimport type { CLIOptions, ChangelogMetadata } from './types.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf-8'));\n\nconst program = new Command();\n\nprogram\n .name('tst-changelog')\n .description('AI-powered changelog generator using OpenRouter')\n .version(pkg.version);\n\nprogram\n .command('init')\n .description('Create a default configuration file')\n .option('-f, --file <name>', 'Config file name', 'tst-changelog.yaml')\n .action((options: { file: string }) => {\n try {\n const result = initConfig(options.file);\n console.log(`Created config file: ${result.configPath}`);\n\n if (result.huskyConfigured) {\n console.log(`Configured husky hook: ${result.huskyHook}`);\n }\n\n console.log('\\nNext steps:');\n console.log('1. Set the OPENROUTER_API_KEY environment variable');\n console.log('2. Adjust settings in the config file as needed');\n if (!result.huskyConfigured) {\n console.log('3. Run: npx tst-changelog generate');\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram\n .command('generate', { isDefault: true })\n .description('Generate changelog from staged changes or commit history')\n .option('-c, --config <path>', 'Path to config file')\n .option('-d, --dry-run', 'Preview without modifying files')\n .option('-v, --verbose', 'Enable verbose output')\n .option('--from-commits <count>', 'Generate from last N commits instead of staged changes', parseInt)\n .action(run);\n\nasync function run(options: CLIOptions): Promise<void> {\n const verbose = options.verbose ?? false;\n\n try {\n // Load configuration\n if (verbose) console.log('Loading configuration...');\n const config = loadConfig(options.config);\n if (verbose) console.log('Config loaded:', JSON.stringify(config, null, 2));\n\n // Initialize services\n const git = new GitService(config.git);\n const ai = new AIService(config.ai);\n const changelog = new ChangelogService(config.changelog);\n\n // Check if we're in a git repo\n if (!(await git.isGitRepo())) {\n console.error('Error: Not a git repository');\n process.exit(1);\n }\n\n let commits;\n let staged;\n\n if (options.fromCommits) {\n // Generate from commit history\n if (verbose) console.log(`Getting last ${options.fromCommits} commits...`);\n commits = await git.getRecentCommits(options.fromCommits);\n\n if (commits.length === 0) {\n console.log('No commits found.');\n process.exit(0);\n }\n\n if (verbose) console.log(`Found ${commits.length} commits`);\n\n // Empty staged for history mode\n staged = { files: [], diff: '' };\n } else {\n // Get staged changes (default behavior)\n if (verbose) console.log('Getting staged changes...');\n staged = await git.getStagedChanges();\n\n if (staged.files.length === 0) {\n console.log('No staged changes found. Stage some changes first.');\n process.exit(0);\n }\n\n if (verbose) {\n console.log(`Staged files: ${staged.files.join(', ')}`);\n console.log(`Diff length: ${staged.diff.length} chars`);\n }\n\n // Get recent commits for context\n if (verbose) console.log('Getting recent commits...');\n commits = await git.getUnmergedCommits();\n if (verbose) console.log(`Found ${commits.length} unmerged commits`);\n }\n\n // Generate changelog using AI\n console.log('Generating changelog with AI...');\n const generated = await ai.generateChangelog(commits, staged);\n\n if (generated.sections.length === 0) {\n console.log('No changelog entries generated.');\n if (verbose) console.log('Raw AI response:', generated.raw);\n process.exit(0);\n }\n\n if (verbose) {\n console.log('Generated sections:');\n for (const section of generated.sections) {\n console.log(` ${section.type}:`);\n for (const item of section.items) {\n console.log(` - ${item}`);\n }\n }\n }\n\n // Collect metadata for changelog entry\n // Skip author/commit for --from-commits mode since it covers multiple commits\n let metadata: ChangelogMetadata = { timestamp: new Date() };\n\n if (!options.fromCommits) {\n const [author, commitHash] = await Promise.all([\n git.getCurrentUser(),\n git.getHeadCommit(),\n ]);\n metadata = {\n author: author || undefined,\n commitHash: commitHash || undefined,\n timestamp: new Date(),\n };\n }\n\n if (verbose) {\n console.log('Metadata:', metadata);\n }\n\n // Update changelog (MD)\n const newContent = changelog.update(generated, undefined, metadata);\n\n // Update changelog (JSON)\n const jsonData = changelog.updateJson(generated, undefined, metadata);\n\n if (options.dryRun) {\n console.log('\\n--- DRY RUN ---');\n console.log('Would update changelog at:', changelog.getFilePath());\n if (changelog.getJsonFilePath()) {\n console.log('Would update JSON at:', changelog.getJsonFilePath());\n }\n console.log('\\n--- New MD entry ---');\n console.log(changelog.formatEntry(generated, undefined, metadata));\n console.log('\\n--- New JSON entry ---');\n console.log(JSON.stringify(jsonData.releases[0], null, 2));\n console.log('--- End ---\\n');\n process.exit(0);\n }\n\n // Write changelog (MD)\n changelog.write(newContent);\n console.log(`Updated: ${changelog.getFilePath()}`);\n\n // Write changelog (JSON)\n if (changelog.getJsonFilePath()) {\n changelog.writeJson(jsonData);\n console.log(`Updated: ${changelog.getJsonFilePath()}`);\n }\n\n // Only auto-stage when using staged changes mode (not --from-commits)\n if (!options.fromCommits) {\n await git.stageFile(config.changelog.file);\n if (config.changelog.jsonFile) {\n await git.stageFile(config.changelog.jsonFile);\n }\n console.log('Staged changelog files');\n }\n\n console.log('Done!');\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n if (verbose && error instanceof Error) {\n console.error(error.stack);\n }\n process.exit(1);\n }\n}\n\nprogram.parse();\n\n// Export for programmatic usage\nexport { loadConfig, initConfig, type InitResult } from './config.js';\nexport { GitService } from './git.js';\nexport { AIService } from './ai.js';\nexport { ChangelogService } from './changelog.js';\nexport * from './types.js';\n","import { readFileSync, existsSync, writeFileSync, appendFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parse, stringify } from 'yaml';\nimport type { Config } from './types.js';\n\nconst DEFAULT_PROMPT = `You are a changelog generator. Your task is to analyze git commits and staged changes to generate changelog entries following the Keep a Changelog format.\n\nOutput Format:\nReturn ONLY a JSON object with the following structure:\n{\n \"sections\": [\n {\n \"type\": \"Added\" | \"Changed\" | \"Deprecated\" | \"Removed\" | \"Fixed\" | \"Security\",\n \"items\": [\"description (by Author, YYYY-MM-DD HH:MM, hash)\", \"description 2 (by Author, YYYY-MM-DD HH:MM, hash)\"]\n }\n ]\n}\n\nGuidelines:\n- Use present tense (e.g., \"Add feature\" not \"Added feature\")\n- Be concise but descriptive\n- Group related changes together\n- Focus on user-facing changes\n- Ignore merge commits and trivial changes\n- IMPORTANT: Each item MUST include author, datetime, and commit hash from the input in the format: \"description (by Author, YYYY-MM-DD HH:MM, hash)\"\n- If multiple commits contribute to the same change, list them: \"description (by Author1, datetime1, hash1; Author2, datetime2, hash2)\"\n- Parse conventional commits (feat:, fix:, etc.) into appropriate sections:\n - feat: -> Added\n - fix: -> Fixed\n - docs:, style:, refactor:, perf:, test:, chore: -> Changed\n - BREAKING CHANGE: -> Changed (mention breaking)\n - deprecate: -> Deprecated\n - remove: -> Removed\n - security: -> Security`;\n\nconst DEFAULT_CONFIG: Config = {\n ai: {\n provider: 'openrouter',\n model: 'anthropic/claude-sonnet-4.5',\n token: '${OPENROUTER_API_KEY}',\n prompt: DEFAULT_PROMPT,\n },\n changelog: {\n file: 'CHANGELOG.md',\n jsonFile: 'changelog.json',\n format: 'keepachangelog',\n includeDate: true,\n includeTime: true,\n includeAuthor: true,\n includeCommitLink: true,\n repoUrl: '',\n groupBy: 'type',\n },\n git: {\n baseBranch: 'main',\n analyzeDepth: 50,\n },\n};\n\n/**\n * Resolve environment variables in config values\n * Supports ${ENV_VAR} syntax\n */\nfunction resolveEnvVars(value: string): string {\n return value.replace(/\\$\\{([^}]+)\\}/g, (_, envVar) => {\n const envValue = process.env[envVar];\n if (!envValue) {\n throw new Error(`Environment variable ${envVar} is not set`);\n }\n return envValue;\n });\n}\n\n/**\n * Recursively resolve env vars in object\n */\nfunction resolveConfigEnvVars<T>(obj: T): T {\n if (typeof obj === 'string') {\n return resolveEnvVars(obj) as T;\n }\n if (Array.isArray(obj)) {\n return obj.map(resolveConfigEnvVars) as T;\n }\n if (obj && typeof obj === 'object') {\n const resolved: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n resolved[key] = resolveConfigEnvVars(value);\n }\n return resolved as T;\n }\n return obj;\n}\n\n/**\n * Deep merge two objects\n */\nfunction deepMerge(target: Config, source: Partial<Config>): Config {\n return {\n ai: { ...target.ai, ...source.ai },\n changelog: { ...target.changelog, ...source.changelog },\n git: { ...target.git, ...source.git },\n };\n}\n\n/**\n * Load configuration from yaml file\n */\nexport function loadConfig(configPath?: string): Config {\n const defaultPaths = ['tst-changelog.yaml', 'tst-changelog.yml', '.tst-changelog.yaml'];\n\n let filePath: string | undefined;\n\n if (configPath) {\n filePath = resolve(process.cwd(), configPath);\n if (!existsSync(filePath)) {\n throw new Error(`Config file not found: ${filePath}`);\n }\n } else {\n for (const p of defaultPaths) {\n const fullPath = resolve(process.cwd(), p);\n if (existsSync(fullPath)) {\n filePath = fullPath;\n break;\n }\n }\n }\n\n if (!filePath) {\n return resolveConfigEnvVars(DEFAULT_CONFIG);\n }\n\n const content = readFileSync(filePath, 'utf-8');\n const parsed = parse(content) as Partial<Config>;\n\n const merged = deepMerge(DEFAULT_CONFIG, parsed);\n return resolveConfigEnvVars(merged);\n}\n\nexport interface InitResult {\n configPath: string;\n huskyConfigured: boolean;\n huskyHook?: string;\n}\n\n/**\n * Initialize a new config file with defaults\n */\nexport function initConfig(fileName = 'tst-changelog.yaml'): InitResult {\n const filePath = resolve(process.cwd(), fileName);\n\n if (existsSync(filePath)) {\n throw new Error(`Config file already exists: ${filePath}`);\n }\n\n const configTemplate: Config = {\n ai: {\n provider: 'openrouter',\n model: 'anthropic/claude-sonnet-4.5',\n token: '${OPENROUTER_API_KEY}',\n prompt: DEFAULT_PROMPT,\n },\n changelog: {\n file: 'CHANGELOG.md',\n jsonFile: 'changelog.json',\n format: 'keepachangelog',\n includeDate: true,\n includeTime: true,\n includeAuthor: true,\n includeCommitLink: true,\n repoUrl: '',\n groupBy: 'type',\n },\n git: {\n baseBranch: 'main',\n analyzeDepth: 50,\n },\n };\n\n const content = stringify(configTemplate, { lineWidth: 0 });\n writeFileSync(filePath, content, 'utf-8');\n\n // Check for husky and configure hook\n const huskyResult = configureHusky();\n\n return {\n configPath: filePath,\n ...huskyResult,\n };\n}\n\n/**\n * Configure husky pre-commit hook if husky is present\n */\nfunction configureHusky(): { huskyConfigured: boolean; huskyHook?: string } {\n const huskyDir = resolve(process.cwd(), '.husky');\n\n if (!existsSync(huskyDir)) {\n return { huskyConfigured: false };\n }\n\n const preCommitPath = resolve(huskyDir, 'pre-commit');\n const commands = `npx tst-changelog generate\ngit add CHANGELOG.md changelog.json 2>/dev/null || true`;\n\n // Check if hook already exists\n if (existsSync(preCommitPath)) {\n const content = readFileSync(preCommitPath, 'utf-8');\n if (content.includes('tst-changelog')) {\n return { huskyConfigured: false }; // Already configured\n }\n // Append to existing hook\n appendFileSync(preCommitPath, `\\n${commands}\\n`);\n } else {\n // Create new hook\n writeFileSync(preCommitPath, `#!/usr/bin/env sh\\n. \"$(dirname -- \"$0\")/_/husky.sh\"\\n\\n${commands}\\n`, { mode: 0o755 });\n }\n\n return { huskyConfigured: true, huskyHook: preCommitPath };\n}\n","import simpleGit, { SimpleGit } from 'simple-git';\nimport type { CommitInfo, StagedChanges, GitConfig } from './types.js';\n\nexport class GitService {\n private git: SimpleGit;\n private config: GitConfig;\n\n constructor(config: GitConfig, cwd?: string) {\n this.git = simpleGit(cwd);\n this.config = config;\n }\n\n /**\n * Get list of staged files\n */\n async getStagedFiles(): Promise<string[]> {\n const status = await this.git.status();\n return status.staged;\n }\n\n /**\n * Get diff of staged changes\n */\n async getStagedDiff(): Promise<string> {\n const diff = await this.git.diff(['--cached']);\n return diff;\n }\n\n /**\n * Get staged changes (files + diff)\n */\n async getStagedChanges(): Promise<StagedChanges> {\n const [files, diff] = await Promise.all([\n this.getStagedFiles(),\n this.getStagedDiff(),\n ]);\n return { files, diff };\n }\n\n /**\n * Get recent commits from the base branch\n */\n async getRecentCommits(count?: number): Promise<CommitInfo[]> {\n const limit = count ?? this.config.analyzeDepth;\n const log = await this.git.log({\n maxCount: limit,\n format: {\n hash: '%H',\n message: '%s',\n author: '%an',\n date: '%aI',\n body: '%b',\n },\n });\n\n return log.all.map((commit) => ({\n hash: commit.hash,\n message: commit.message,\n author: commit.author,\n date: commit.date,\n body: commit.body?.trim() || undefined,\n }));\n }\n\n /**\n * Get commits not yet in base branch (for feature branches)\n */\n async getUnmergedCommits(): Promise<CommitInfo[]> {\n try {\n const log = await this.git.log({\n from: this.config.baseBranch,\n to: 'HEAD',\n format: {\n hash: '%H',\n message: '%s',\n author: '%an',\n date: '%aI',\n body: '%b',\n },\n });\n\n return log.all.map((commit) => ({\n hash: commit.hash,\n message: commit.message,\n author: commit.author,\n date: commit.date,\n body: commit.body?.trim() || undefined,\n }));\n } catch {\n // If base branch doesn't exist, return empty\n return [];\n }\n }\n\n /**\n * Get current branch name\n */\n async getCurrentBranch(): Promise<string> {\n const branch = await this.git.revparse(['--abbrev-ref', 'HEAD']);\n return branch.trim();\n }\n\n /**\n * Stage a file\n */\n async stageFile(filePath: string): Promise<void> {\n await this.git.add(filePath);\n }\n\n /**\n * Check if we're in a git repository\n */\n async isGitRepo(): Promise<boolean> {\n try {\n await this.git.revparse(['--git-dir']);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get current git user name\n */\n async getCurrentUser(): Promise<string> {\n try {\n const name = await this.git.getConfig('user.name');\n return name.value ?? '';\n } catch {\n return '';\n }\n }\n\n /**\n * Get HEAD commit hash\n */\n async getHeadCommit(): Promise<string> {\n try {\n const hash = await this.git.revparse(['HEAD']);\n return hash.trim();\n } catch {\n return '';\n }\n }\n}\n","import type { AIConfig, CommitInfo, StagedChanges, GeneratedChangelog, ChangelogSection, ChangeType } from './types.js';\n\nconst OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';\n\ninterface OpenRouterResponse {\n choices: Array<{\n message: {\n content: string;\n };\n }>;\n}\n\nexport class AIService {\n private config: AIConfig;\n\n constructor(config: AIConfig) {\n this.config = config;\n }\n\n /**\n * Generate changelog entries from commits and staged changes\n */\n async generateChangelog(\n commits: CommitInfo[],\n staged: StagedChanges\n ): Promise<GeneratedChangelog> {\n const userPrompt = this.buildPrompt(commits, staged);\n const response = await this.callOpenRouter(userPrompt);\n return this.parseResponse(response);\n }\n\n private buildPrompt(commits: CommitInfo[], staged: StagedChanges): string {\n const parts: string[] = [];\n\n if (commits.length > 0) {\n parts.push('## Recent Commits:');\n for (const commit of commits) {\n const shortHash = commit.hash.slice(0, 7);\n const [date, timePart] = commit.date.split('T');\n const time = timePart?.slice(0, 5) ?? ''; // HH:MM\n const datetime = time ? `${date} ${time}` : date;\n parts.push(`- [${shortHash}] (${commit.author}, ${datetime}) ${commit.message}`);\n if (commit.body) {\n parts.push(` ${commit.body}`);\n }\n }\n }\n\n if (staged.files.length > 0) {\n parts.push('\\n## Staged Files:');\n parts.push(staged.files.join('\\n'));\n }\n\n if (staged.diff) {\n // Limit diff size to avoid token limits\n const maxDiffLength = 4000;\n const truncatedDiff = staged.diff.length > maxDiffLength\n ? staged.diff.slice(0, maxDiffLength) + '\\n... (truncated)'\n : staged.diff;\n parts.push('\\n## Staged Diff:');\n parts.push('```diff');\n parts.push(truncatedDiff);\n parts.push('```');\n }\n\n parts.push('\\nGenerate changelog entries for these changes.');\n\n return parts.join('\\n');\n }\n\n private async callOpenRouter(prompt: string): Promise<string> {\n const response = await fetch(OPENROUTER_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.token}`,\n 'HTTP-Referer': 'https://github.com/testudosrl/tst-libs',\n 'X-Title': 'tst-changelog',\n },\n body: JSON.stringify({\n model: this.config.model,\n messages: [\n { role: 'system', content: this.config.prompt ?? '' },\n { role: 'user', content: prompt },\n ],\n temperature: 0.3,\n max_tokens: 4000,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`OpenRouter API error: ${response.status} - ${error}`);\n }\n\n const data = (await response.json()) as OpenRouterResponse;\n return data.choices[0]?.message?.content ?? '';\n }\n\n private parseResponse(response: string): GeneratedChangelog {\n try {\n // Extract JSON from response (handle markdown code blocks or raw JSON)\n let jsonStr = response;\n\n // Try markdown code block first\n const codeBlockMatch = response.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n if (codeBlockMatch) {\n jsonStr = codeBlockMatch[1];\n } else {\n // Try to find JSON object in the response\n const jsonObjectMatch = response.match(/\\{[\\s\\S]*\"sections\"[\\s\\S]*\\}/);\n if (jsonObjectMatch) {\n jsonStr = jsonObjectMatch[0];\n }\n }\n\n const parsed = JSON.parse(jsonStr.trim()) as { sections: ChangelogSection[] };\n\n // Validate and normalize sections\n const validTypes: ChangeType[] = ['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security'];\n const sections = parsed.sections\n .filter((s) => validTypes.includes(s.type))\n .map((s) => ({\n type: s.type,\n items: Array.isArray(s.items) ? s.items.filter((i) => typeof i === 'string') : [],\n }))\n .filter((s) => s.items.length > 0);\n\n return {\n raw: response,\n sections,\n };\n } catch (error) {\n console.error('Failed to parse AI response:', error);\n console.error('Raw response:', response);\n return {\n raw: response,\n sections: [],\n };\n }\n }\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type { ChangelogConfig, ChangelogSection, GeneratedChangelog, ChangelogMetadata, ChangelogJson, ChangelogJsonRelease } from './types.js';\n\nconst KEEPACHANGELOG_HEADER = `# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n`;\n\nconst SECTION_ORDER = ['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security'] as const;\n\nexport class ChangelogService {\n private config: ChangelogConfig;\n private filePath: string;\n\n constructor(config: ChangelogConfig, cwd?: string) {\n this.config = config;\n this.filePath = resolve(cwd ?? process.cwd(), config.file);\n }\n\n /**\n * Read existing changelog content\n */\n read(): string | null {\n if (!existsSync(this.filePath)) {\n return null;\n }\n return readFileSync(this.filePath, 'utf-8');\n }\n\n /**\n * Generate new changelog entry\n */\n formatEntry(generated: GeneratedChangelog, version?: string, metadata?: ChangelogMetadata): string {\n const versionStr = version ?? 'Unreleased';\n const headerParts: string[] = [`## [${versionStr}]`];\n\n // Date and time\n if (this.config.includeDate) {\n const now = metadata?.timestamp ?? new Date();\n let dateStr = now.toISOString().split('T')[0];\n if (this.config.includeTime) {\n const timeStr = now.toTimeString().split(' ')[0].slice(0, 5); // HH:MM\n dateStr += ` ${timeStr}`;\n }\n headerParts.push(`- ${dateStr}`);\n }\n\n // Author\n if (this.config.includeAuthor && metadata?.author) {\n headerParts.push(`by ${metadata.author}`);\n }\n\n // Commit link\n if (this.config.includeCommitLink && metadata?.commitHash) {\n const shortHash = metadata.commitHash.slice(0, 7);\n if (this.config.repoUrl) {\n const commitUrl = `${this.config.repoUrl.replace(/\\/$/, '')}/commit/${metadata.commitHash}`;\n headerParts.push(`([${shortHash}](${commitUrl}))`);\n } else {\n headerParts.push(`(${shortHash})`);\n }\n }\n\n const headerLine = headerParts.join(' ');\n\n const sections = this.sortSections(generated.sections);\n const sectionLines = sections\n .map((section) => this.formatSection(section))\n .join('\\n\\n');\n\n return `${headerLine}\\n\\n${sectionLines}`;\n }\n\n private sortSections(sections: ChangelogSection[]): ChangelogSection[] {\n return [...sections].sort((a, b) => {\n const indexA = SECTION_ORDER.indexOf(a.type as typeof SECTION_ORDER[number]);\n const indexB = SECTION_ORDER.indexOf(b.type as typeof SECTION_ORDER[number]);\n return indexA - indexB;\n });\n }\n\n private formatSection(section: ChangelogSection): string {\n const items = section.items.map((item) => `- ${this.formatItemWithLinks(item)}`).join('\\n');\n return `### ${section.type}\\n\\n${items}`;\n }\n\n /**\n * Convert commit hashes in item to markdown links if repoUrl is configured\n */\n private formatItemWithLinks(item: string): string {\n if (!this.config.repoUrl) return item;\n\n const repoUrl = this.config.repoUrl.replace(/\\/$/, '');\n\n // Match patterns like \"abc1234\" or \"abc1234; def5678\" at the end of parentheses\n // Pattern: 7-char hex hash\n return item.replace(/\\b([a-f0-9]{7})\\b/g, (match) => {\n return `[${match}](${repoUrl}/commit/${match})`;\n });\n }\n\n /**\n * Update or create changelog with new entry\n */\n update(generated: GeneratedChangelog, version?: string, metadata?: ChangelogMetadata): string {\n const existing = this.read();\n\n if (!existing) {\n // Create new changelog\n const newEntry = this.formatEntry(generated, version, metadata);\n return KEEPACHANGELOG_HEADER + newEntry + '\\n';\n }\n\n // Find where to insert new entry (after header, before first version)\n const lines = existing.split('\\n');\n let insertIndex = -1;\n\n // Look for ## [Unreleased] or first ## [ section\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.startsWith('## [Unreleased]')) {\n // Merge with existing unreleased section\n const endIndex = this.findSectionEnd(lines, i + 1);\n const existingSectionLines = lines.slice(i, endIndex);\n const mergedSection = this.mergeUnreleasedSection(existingSectionLines, generated, metadata);\n const before = lines.slice(0, i).join('\\n');\n const after = lines.slice(endIndex).join('\\n');\n return before + (before.endsWith('\\n') ? '' : '\\n') + mergedSection + '\\n' + after;\n }\n if (line.startsWith('## [') && insertIndex === -1) {\n insertIndex = i;\n break;\n }\n }\n\n const newEntry = this.formatEntry(generated, version, metadata);\n\n if (insertIndex === -1) {\n // No version sections found, append after header\n return existing.trimEnd() + '\\n\\n' + newEntry + '\\n';\n }\n\n // Insert before first version\n const before = lines.slice(0, insertIndex).join('\\n');\n const after = lines.slice(insertIndex).join('\\n');\n return before + (before.endsWith('\\n') ? '' : '\\n') + newEntry + '\\n\\n' + after;\n }\n\n /**\n * Merge new entries into existing Unreleased section\n */\n private mergeUnreleasedSection(\n existingLines: string[],\n generated: GeneratedChangelog,\n metadata?: ChangelogMetadata\n ): string {\n // Parse existing sections\n const existingSections: Map<string, string[]> = new Map();\n let currentSection: string | null = null;\n\n for (const line of existingLines) {\n if (line.startsWith('### ')) {\n currentSection = line.slice(4).trim();\n if (!existingSections.has(currentSection)) {\n existingSections.set(currentSection, []);\n }\n } else if (currentSection && line.startsWith('- ')) {\n existingSections.get(currentSection)!.push(line.slice(2));\n }\n }\n\n // Merge new sections\n for (const section of generated.sections) {\n const existing = existingSections.get(section.type) ?? [];\n // Add new items that don't already exist\n for (const item of section.items) {\n if (!existing.includes(item)) {\n existing.push(item);\n }\n }\n existingSections.set(section.type, existing);\n }\n\n // Build merged entry\n const versionStr = 'Unreleased';\n const headerParts: string[] = [`## [${versionStr}]`];\n\n if (this.config.includeDate) {\n const now = metadata?.timestamp ?? new Date();\n let dateStr = now.toISOString().split('T')[0];\n if (this.config.includeTime) {\n const timeStr = now.toTimeString().split(' ')[0].slice(0, 5);\n dateStr += ` ${timeStr}`;\n }\n headerParts.push(`- ${dateStr}`);\n }\n\n if (this.config.includeAuthor && metadata?.author) {\n headerParts.push(`by ${metadata.author}`);\n }\n\n if (this.config.includeCommitLink && metadata?.commitHash) {\n const shortHash = metadata.commitHash.slice(0, 7);\n if (this.config.repoUrl) {\n const commitUrl = `${this.config.repoUrl.replace(/\\/$/, '')}/commit/${metadata.commitHash}`;\n headerParts.push(`([${shortHash}](${commitUrl}))`);\n } else {\n headerParts.push(`(${shortHash})`);\n }\n }\n\n const headerLine = headerParts.join(' ');\n\n // Sort and format sections\n const sortedSections: ChangelogSection[] = [];\n for (const type of SECTION_ORDER) {\n const items = existingSections.get(type);\n if (items && items.length > 0) {\n sortedSections.push({ type, items });\n }\n }\n\n const sectionLines = sortedSections\n .map((section) => this.formatSection(section))\n .join('\\n\\n');\n\n return `${headerLine}\\n\\n${sectionLines}`;\n }\n\n private findSectionEnd(lines: string[], startIndex: number): number {\n for (let i = startIndex; i < lines.length; i++) {\n if (lines[i].startsWith('## [')) {\n return i;\n }\n }\n return lines.length;\n }\n\n /**\n * Write changelog to file\n */\n write(content: string): void {\n writeFileSync(this.filePath, content, 'utf-8');\n }\n\n /**\n * Get the file path\n */\n getFilePath(): string {\n return this.filePath;\n }\n\n /**\n * Get JSON file path if configured\n */\n getJsonFilePath(): string | null {\n if (!this.config.jsonFile) return null;\n return resolve(process.cwd(), this.config.jsonFile);\n }\n\n /**\n * Read existing JSON changelog\n */\n readJson(): ChangelogJson | null {\n const jsonPath = this.getJsonFilePath();\n if (!jsonPath || !existsSync(jsonPath)) {\n return null;\n }\n try {\n const content = readFileSync(jsonPath, 'utf-8');\n return JSON.parse(content) as ChangelogJson;\n } catch {\n return null;\n }\n }\n\n /**\n * Create a JSON release entry from generated changelog\n */\n createJsonRelease(generated: GeneratedChangelog, version?: string, metadata?: ChangelogMetadata): ChangelogJsonRelease {\n const now = metadata?.timestamp ?? new Date();\n const sections: Record<string, string[]> = {};\n\n for (const section of generated.sections) {\n sections[section.type] = section.items;\n }\n\n const release: ChangelogJsonRelease = {\n version: version ?? 'Unreleased',\n date: now.toISOString().split('T')[0],\n sections,\n };\n\n if (this.config.includeTime) {\n release.time = now.toTimeString().split(' ')[0].slice(0, 5);\n }\n\n if (this.config.includeAuthor && metadata?.author) {\n release.author = metadata.author;\n }\n\n if (this.config.includeCommitLink && metadata?.commitHash) {\n release.commitHash = metadata.commitHash;\n }\n\n return release;\n }\n\n /**\n * Update JSON changelog with new release\n */\n updateJson(generated: GeneratedChangelog, version?: string, metadata?: ChangelogMetadata): ChangelogJson {\n const newRelease = this.createJsonRelease(generated, version, metadata);\n const existing = this.readJson();\n\n if (!existing) {\n return { releases: [newRelease] };\n }\n\n // Find and merge or prepend\n const unreleasedIndex = existing.releases.findIndex(r => r.version === 'Unreleased');\n\n if (unreleasedIndex >= 0 && newRelease.version === 'Unreleased') {\n // Merge with existing unreleased\n const existingRelease = existing.releases[unreleasedIndex];\n for (const [type, items] of Object.entries(newRelease.sections)) {\n const existingItems = existingRelease.sections[type] ?? [];\n for (const item of items) {\n if (!existingItems.includes(item)) {\n existingItems.push(item);\n }\n }\n existingRelease.sections[type] = existingItems;\n }\n // Update metadata to latest\n existingRelease.date = newRelease.date;\n if (newRelease.time) existingRelease.time = newRelease.time;\n if (newRelease.author) existingRelease.author = newRelease.author;\n if (newRelease.commitHash) existingRelease.commitHash = newRelease.commitHash;\n } else {\n // Prepend new release\n existing.releases.unshift(newRelease);\n }\n\n return existing;\n }\n\n /**\n * Write JSON changelog to file\n */\n writeJson(data: ChangelogJson): void {\n const jsonPath = this.getJsonFilePath();\n if (!jsonPath) return;\n writeFileSync(jsonPath, JSON.stringify(data, null, 2), 'utf-8');\n }\n}\n"],"mappings":"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","names":["readFileSync","resolve","readFileSync","writeFileSync","existsSync","resolve","newEntry","before","after","readFileSync","resolve"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tst-changelog",
3
- "version": "0.1.3",
3
+ "version": "0.3.0",
4
4
  "description": "AI-powered changelog generator using OpenRouter",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",