@eldrforge/ai-service 0.1.22 → 1.0.1

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/README.md CHANGED
@@ -1250,3 +1250,5 @@ Apache-2.0
1250
1250
  ## Changelog
1251
1251
 
1252
1252
  See [RELEASE_NOTES.md](./RELEASE_NOTES.md) for version history and changes.
1253
+
1254
+ <!-- Build: 2026-01-15 15:59:12 UTC -->
package/dist/index.js CHANGED
@@ -1680,9 +1680,11 @@ function createGetFileModificationTimesTool() {
1680
1680
  async function runAgenticCommit(config) {
1681
1681
  const {
1682
1682
  changedFiles,
1683
- diffContent,
1683
+ diffContent: _diffContent,
1684
+ // Optional - AI can use tools to read files
1684
1685
  userDirection,
1685
- logContext,
1686
+ logContext: _logContext,
1687
+ // Optional - AI can use get_recent_commits tool
1686
1688
  model = "gpt-4o",
1687
1689
  maxIterations = 10,
1688
1690
  debug = false,
@@ -1700,14 +1702,8 @@ async function runAgenticCommit(config) {
1700
1702
  });
1701
1703
  const tools = createCommitTools();
1702
1704
  toolRegistry.registerAll(tools);
1703
- const toolGuidance = generateToolGuidance(tools, {
1704
- strategy: "adaptive",
1705
- includeExamples: true,
1706
- explainWhenToUse: true,
1707
- includeCategories: true
1708
- });
1709
- const systemPrompt = buildSystemPrompt$3(toolGuidance);
1710
- const userMessage = buildUserMessage$2(changedFiles, diffContent, userDirection, logContext);
1705
+ const systemPrompt = buildSystemPrompt$3();
1706
+ const userMessage = buildUserMessage$2(changedFiles, userDirection);
1711
1707
  const messages = [
1712
1708
  { role: "system", content: systemPrompt },
1713
1709
  { role: "user", content: userMessage }
@@ -1741,10 +1737,22 @@ async function runAgenticCommit(config) {
1741
1737
  toolMetrics: result.toolMetrics
1742
1738
  };
1743
1739
  }
1744
- function buildSystemPrompt$3(toolGuidance) {
1740
+ function buildSystemPrompt$3() {
1745
1741
  return `You are a professional software engineer writing commit messages for your team.
1746
1742
 
1747
- ${toolGuidance}
1743
+ ## Available Tools
1744
+
1745
+ You have access to tools to investigate the changes:
1746
+ - \`get_file_content\` - Read file contents or diffs for specific files
1747
+ - \`get_file_modification_times\` - See when files were modified relative to each other
1748
+ - \`group_files_by_concern\` - Understand how files relate by type/purpose
1749
+ - \`get_recent_commits\` - Get recent commit history to avoid duplicates
1750
+ - \`get_file_history\` - Understand how code evolved
1751
+ - \`get_file_dependencies\` - Assess impact of changes
1752
+ - \`get_related_tests\` - Find related test files
1753
+ - \`search_codebase\` - Search for usage patterns
1754
+
1755
+ Use these tools to investigate the changes rather than asking for information upfront.
1748
1756
 
1749
1757
  ## Your Task
1750
1758
 
@@ -1850,37 +1858,30 @@ Split 3:
1850
1858
 
1851
1859
  Output only the commit message and splits. No conversational remarks or follow-up offers.`;
1852
1860
  }
1853
- function buildUserMessage$2(changedFiles, diffContent, userDirection, logContext) {
1861
+ function buildUserMessage$2(changedFiles, userDirection) {
1854
1862
  const fileCount = changedFiles.length;
1855
1863
  const manyFiles = fileCount >= 5;
1856
1864
  let message = `I have staged changes that need a commit message.
1857
1865
 
1858
1866
  Changed files (${fileCount}):
1859
- ${changedFiles.map((f) => ` - ${f}`).join("\n")}
1860
-
1861
- Diff:
1862
- ${diffContent}`;
1867
+ ${changedFiles.map((f) => ` - ${f}`).join("\n")}`;
1863
1868
  if (userDirection) {
1864
1869
  message += `
1865
1870
 
1866
1871
  User direction: ${userDirection}`;
1867
- }
1868
- if (logContext) {
1869
- message += `
1870
-
1871
- Recent commit history for context:
1872
- ${logContext}`;
1873
1872
  }
1874
1873
  message += `
1875
1874
 
1876
1875
  ## Your Analysis Task
1877
1876
 
1878
- ${manyFiles ? `With ${fileCount} files changed, consider whether these represent multiple distinct changes that should be split into separate commits.
1877
+ ${manyFiles ? `With ${fileCount} files changed, investigate whether these represent multiple distinct changes that should be split into separate commits.
1879
1878
 
1880
- ` : ""}Gather signals to understand the changes:
1881
- 1. Use \`get_file_modification_times\` to see when files were modified relative to each other
1882
- 2. Use \`group_files_by_concern\` to understand how files relate by type/purpose
1883
- 3. Cross-reference both signals - temporal proximity and logical relatedness - to determine the best commit structure
1879
+ ` : ""}Use your tools to investigate the changes:
1880
+ 1. Use \`get_file_content\` to read the actual changes in the files
1881
+ 2. Use \`get_file_modification_times\` to see when files were modified relative to each other
1882
+ 3. Use \`group_files_by_concern\` to understand how files relate by type/purpose
1883
+ 4. Use \`get_recent_commits\` to check recent commit history and avoid duplicates
1884
+ 5. Cross-reference temporal proximity and logical relatedness to determine the best commit structure
1884
1885
 
1885
1886
  Consider:
1886
1887
  - What distinct features, fixes, or improvements are included?
@@ -1889,8 +1890,7 @@ Consider:
1889
1890
 
1890
1891
  ${manyFiles ? "With many files, consider whether multiple focused commits would be clearer than one large commit." : "If changes represent multiple logical concerns, suggest splits."}
1891
1892
 
1892
- If context information is provided, use it only if relevant to these specific changes.
1893
- Don't force connections that don't exist - focus on what actually changed.`;
1893
+ Start by reading the file contents to understand what actually changed.`;
1894
1894
  return message;
1895
1895
  }
1896
1896
  function sanitizeFilePath(filePath) {
@@ -2596,11 +2596,14 @@ async function runAgenticRelease(config) {
2596
2596
  const {
2597
2597
  fromRef,
2598
2598
  toRef,
2599
- logContent,
2600
- diffContent,
2599
+ logContent: _logContent,
2600
+ // Optional - AI can use tools to get commit log
2601
+ diffContent: _diffContent,
2602
+ // Optional - AI can use tools to get diffs
2601
2603
  milestoneIssues,
2602
2604
  releaseFocus,
2603
2605
  userContext,
2606
+ targetVersion,
2604
2607
  model = "gpt-4o",
2605
2608
  maxIterations = 30,
2606
2609
  debug = false,
@@ -2618,21 +2621,14 @@ async function runAgenticRelease(config) {
2618
2621
  });
2619
2622
  const tools = createReleaseTools();
2620
2623
  toolRegistry.registerAll(tools);
2621
- const toolGuidance = generateToolGuidance(tools, {
2622
- strategy: "adaptive",
2623
- includeExamples: true,
2624
- explainWhenToUse: true,
2625
- includeCategories: true
2626
- });
2627
- const systemPrompt = buildSystemPrompt$2(toolGuidance);
2624
+ const systemPrompt = buildSystemPrompt$2();
2628
2625
  const userMessage = buildUserMessage$1({
2629
2626
  fromRef,
2630
2627
  toRef,
2631
- logContent,
2632
- diffContent,
2633
2628
  milestoneIssues,
2634
2629
  releaseFocus,
2635
- userContext
2630
+ userContext,
2631
+ targetVersion
2636
2632
  });
2637
2633
  const messages = [
2638
2634
  { role: "system", content: systemPrompt },
@@ -2666,10 +2662,26 @@ async function runAgenticRelease(config) {
2666
2662
  toolMetrics: result.toolMetrics
2667
2663
  };
2668
2664
  }
2669
- function buildSystemPrompt$2(toolGuidance) {
2665
+ function buildSystemPrompt$2() {
2670
2666
  return `You are a professional software engineer writing release notes for your team and users.
2671
2667
 
2672
- ${toolGuidance}
2668
+ ## Available Tools
2669
+
2670
+ You have access to tools to investigate the release:
2671
+ - \`get_tag_history\` - Understand release patterns
2672
+ - \`get_release_stats\` - Quantify scope of changes
2673
+ - \`compare_previous_release\` - See how this compares to previous releases
2674
+ - \`group_files_by_concern\` - Identify themes in changes
2675
+ - \`analyze_commit_patterns\` - Detect patterns in commits
2676
+ - \`get_file_content\` - Read file contents or diffs
2677
+ - \`analyze_diff_section\` - Expand unclear changes
2678
+ - \`get_file_history\` - Understand how code evolved
2679
+ - \`get_file_dependencies\` - Assess impact and reach
2680
+ - \`search_codebase\` - Find usage patterns
2681
+ - \`get_related_tests\` - Understand behavior changes
2682
+ - \`get_breaking_changes\` - Identify breaking changes (always use this)
2683
+
2684
+ Use these tools to investigate the changes rather than asking for information upfront.
2673
2685
 
2674
2686
  ## Your Task
2675
2687
 
@@ -2731,25 +2743,26 @@ Structure your notes logically:
2731
2743
 
2732
2744
  ## Output Format
2733
2745
 
2734
- When ready, format your response as JSON:
2746
+ When ready, output your release notes in this exact format:
2735
2747
 
2736
- RELEASE_NOTES:
2737
- {
2738
- "title": "Clear, factual title describing the main changes",
2739
- "body": "Detailed release notes in Markdown format"
2740
- }
2748
+ RELEASE_TITLE:
2749
+ [A clear, factual title describing the main changes]
2741
2750
 
2742
- Output only the JSON. No conversational remarks or follow-up offers.`;
2751
+ RELEASE_BODY:
2752
+ [Detailed release notes in Markdown format]
2753
+
2754
+ Do not include any JSON, code blocks, or wrapper syntax. Output plain text and markdown only.
2755
+ No conversational remarks or follow-up offers.`;
2743
2756
  }
2744
2757
  function buildUserMessage$1(params) {
2745
- const { fromRef, toRef, logContent, diffContent, milestoneIssues, releaseFocus, userContext } = params;
2746
- let message = `I need comprehensive release notes for changes from ${fromRef} to ${toRef}.
2747
-
2748
- ## Commit Log
2749
- ${logContent}
2758
+ const { fromRef, toRef, milestoneIssues, releaseFocus, userContext, targetVersion } = params;
2759
+ let message = `I need comprehensive release notes for changes from ${fromRef} to ${toRef}.`;
2760
+ if (targetVersion) {
2761
+ const versionNumber = targetVersion.replace(/^v/, "");
2762
+ message += `
2750
2763
 
2751
- ## Diff Summary
2752
- ${diffContent}`;
2764
+ **CRITICAL VERSION INSTRUCTION: This is a release for version ${versionNumber}. You MUST use EXACTLY "${versionNumber}" in your release title. DO NOT use any development/prerelease version numbers you may see in the diff (like ${versionNumber}-dev.0 or similar). The release version is ${versionNumber}.**`;
2765
+ }
2753
2766
  if (milestoneIssues) {
2754
2767
  message += `
2755
2768
 
@@ -2772,7 +2785,18 @@ ${userContext}`;
2772
2785
  }
2773
2786
  message += `
2774
2787
 
2775
- Analyze these changes and write clear release notes. Consider:
2788
+ ## Your Analysis Task
2789
+
2790
+ Use your tools to investigate the changes between ${fromRef} and ${toRef}:
2791
+ 1. Use \`get_release_stats\` to understand the scope of changes
2792
+ 2. Use \`get_tag_history\` or \`compare_previous_release\` to see release patterns
2793
+ 3. Use \`analyze_commit_patterns\` to detect themes
2794
+ 4. Use \`group_files_by_concern\` to identify logical groupings
2795
+ 5. Use \`get_breaking_changes\` to identify breaking changes (always check this)
2796
+ 6. Use \`get_file_content\` or \`analyze_diff_section\` to understand specific changes
2797
+ 7. Use \`get_file_dependencies\` and \`search_codebase\` to assess impact
2798
+
2799
+ Write clear release notes that answer:
2776
2800
  - What's the main story of this release?
2777
2801
  - What problems does it solve?
2778
2802
  - What's the impact on users and developers?
@@ -2782,37 +2806,29 @@ If context information is provided, use it only if relevant to this specific pac
2782
2806
  Don't force connections that don't exist - if context describes changes in other packages
2783
2807
  or unrelated features, simply ignore it and focus on what actually changed in this release.
2784
2808
 
2785
- Investigate as needed to write accurate, helpful release notes.`;
2809
+ Start by gathering statistics and understanding the scope, then investigate specific changes as needed.`;
2786
2810
  return message;
2787
2811
  }
2788
2812
  function parseAgenticResult(finalMessage) {
2789
- const jsonMatch = finalMessage.match(/RELEASE_NOTES:\s*\n([\s\S]*)/);
2790
- if (jsonMatch) {
2791
- try {
2792
- const jsonStr = jsonMatch[1].trim();
2793
- let parsed;
2794
- try {
2795
- parsed = JSON.parse(jsonStr);
2796
- } catch {
2797
- parsed = null;
2798
- }
2799
- if (parsed && parsed.title && parsed.body) {
2800
- return {
2801
- releaseNotes: {
2802
- title: parsed.title,
2803
- body: parsed.body
2804
- }
2805
- };
2813
+ const titleMatch = finalMessage.match(/RELEASE_TITLE:\s*\n(.*?)(?=\n\nRELEASE_BODY:|\n\s*\nRELEASE_BODY:)/s);
2814
+ const bodyMatch = finalMessage.match(/RELEASE_BODY:\s*\n([\s\S]*)/);
2815
+ if (titleMatch && bodyMatch) {
2816
+ const title2 = titleMatch[1].trim();
2817
+ let body2 = bodyMatch[1].trim();
2818
+ body2 = cleanJsonArtifacts(body2);
2819
+ return {
2820
+ releaseNotes: {
2821
+ title: title2,
2822
+ body: body2
2806
2823
  }
2807
- } catch {
2808
- }
2824
+ };
2809
2825
  }
2810
2826
  const lines = finalMessage.split("\n");
2811
2827
  let title = "";
2812
2828
  let body = "";
2813
2829
  let inBody = false;
2814
2830
  for (const line of lines) {
2815
- if (!title && line.trim() && !line.startsWith("#")) {
2831
+ if (!title && line.trim() && !line.startsWith("#") && !line.startsWith("RELEASE_")) {
2816
2832
  title = line.trim();
2817
2833
  } else if (title && line.trim()) {
2818
2834
  inBody = true;
@@ -2825,6 +2841,7 @@ function parseAgenticResult(finalMessage) {
2825
2841
  title = "Release Notes";
2826
2842
  body = finalMessage;
2827
2843
  }
2844
+ body = cleanJsonArtifacts(body);
2828
2845
  return {
2829
2846
  releaseNotes: {
2830
2847
  title: title.trim(),
@@ -2832,6 +2849,15 @@ function parseAgenticResult(finalMessage) {
2832
2849
  }
2833
2850
  };
2834
2851
  }
2852
+ function cleanJsonArtifacts(text) {
2853
+ text = text.replace(/^\s*\{\s*"title":\s*"[^"]*",?\s*"body":\s*"/m, "");
2854
+ text = text.replace(/"\s*\}\s*$/m, "");
2855
+ text = text.replace(/^```json\s*\n/gm, "");
2856
+ text = text.replace(/^```\s*$/gm, "");
2857
+ text = text.replace(/\\"/g, '"');
2858
+ text = text.replace(/\\n/g, "\n");
2859
+ return text.trim();
2860
+ }
2835
2861
  function createPublishTools() {
2836
2862
  return [
2837
2863
  createCheckGitStatusTool(),