@eldrforge/ai-service 0.1.15 → 0.1.17
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/LICENSE +1 -1
- package/README.md +7 -7
- package/dist/index.js +291 -72
- package/dist/index.js.map +1 -1
- package/dist/src/tools/commit-tools.d.ts.map +1 -1
- package/examples/README.md +3 -3
- package/package.json +4 -4
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ AI-powered content generation library with agentic capabilities for commit messa
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
`@eldrforge/ai-service` is a TypeScript library that provides intelligent content generation powered by OpenAI's GPT models. It was extracted from the [kodrdriv](https://github.com/
|
|
7
|
+
`@eldrforge/ai-service` is a TypeScript library that provides intelligent content generation powered by OpenAI's GPT models. It was extracted from the [kodrdriv](https://github.com/grunnverk/kodrdriv) automation tool to enable standalone use in any Node.js application.
|
|
8
8
|
|
|
9
9
|
### Key Features
|
|
10
10
|
|
|
@@ -1198,12 +1198,12 @@ Monitor usage with the `toolMetrics` data.
|
|
|
1198
1198
|
|
|
1199
1199
|
## Contributing
|
|
1200
1200
|
|
|
1201
|
-
Contributions are welcome! This library was extracted from [kodrdriv](https://github.com/
|
|
1201
|
+
Contributions are welcome! This library was extracted from [kodrdriv](https://github.com/grunnverk/kodrdriv).
|
|
1202
1202
|
|
|
1203
1203
|
### Development Setup
|
|
1204
1204
|
|
|
1205
1205
|
```bash
|
|
1206
|
-
git clone https://github.com/
|
|
1206
|
+
git clone https://github.com/grunnverk/ai-service.git
|
|
1207
1207
|
cd ai-service
|
|
1208
1208
|
npm install
|
|
1209
1209
|
npm run build
|
|
@@ -1224,15 +1224,15 @@ Apache-2.0
|
|
|
1224
1224
|
|
|
1225
1225
|
## Related Projects
|
|
1226
1226
|
|
|
1227
|
-
- **[kodrdriv](https://github.com/
|
|
1227
|
+
- **[kodrdriv](https://github.com/grunnverk/kodrdriv)** - Full automation toolkit that uses this library
|
|
1228
1228
|
- **[@eldrforge/git-tools](https://www.npmjs.com/package/@eldrforge/git-tools)** - Git utility functions
|
|
1229
1229
|
- **[@riotprompt/riotprompt](https://www.npmjs.com/package/@riotprompt/riotprompt)** - Structured prompt builder
|
|
1230
1230
|
|
|
1231
1231
|
## Support
|
|
1232
1232
|
|
|
1233
|
-
- 📖 [Full Documentation](https://github.com/
|
|
1234
|
-
- 🐛 [Issue Tracker](https://github.com/
|
|
1235
|
-
- 💬 [Discussions](https://github.com/
|
|
1233
|
+
- 📖 [Full Documentation](https://github.com/grunnverk/ai-service)
|
|
1234
|
+
- 🐛 [Issue Tracker](https://github.com/grunnverk/ai-service/issues)
|
|
1235
|
+
- 💬 [Discussions](https://github.com/grunnverk/ai-service/discussions)
|
|
1236
1236
|
|
|
1237
1237
|
## Changelog
|
|
1238
1238
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenAI } from "openai";
|
|
2
2
|
import { safeJsonParse, run, localBranchExists, isBranchInSyncWithRemote, safeSyncBranchWithRemote } from "@eldrforge/git-tools";
|
|
3
|
-
import fs$1 from "fs";
|
|
3
|
+
import * as fs$1 from "fs";
|
|
4
|
+
import fs__default from "fs";
|
|
4
5
|
import { spawnSync } from "child_process";
|
|
5
6
|
import * as path from "path";
|
|
6
7
|
import path__default from "path";
|
|
@@ -296,7 +297,7 @@ async function transcribeAudio(filePath, options = { model: "whisper-1" }) {
|
|
|
296
297
|
await options.storage.writeTemp(debugFile, JSON.stringify(requestData, null, 2));
|
|
297
298
|
logger2.debug("Wrote request debug file to %s", debugFile);
|
|
298
299
|
}
|
|
299
|
-
audioStream =
|
|
300
|
+
audioStream = fs__default.createReadStream(filePath);
|
|
300
301
|
if (audioStream && typeof audioStream.on === "function") {
|
|
301
302
|
audioStream.on("error", (streamError) => {
|
|
302
303
|
logger2.error("Audio stream error: %s", streamError.message);
|
|
@@ -1143,7 +1144,8 @@ function createCommitTools() {
|
|
|
1143
1144
|
createGetFileDependenciesTool$1(),
|
|
1144
1145
|
createAnalyzeDiffSectionTool$1(),
|
|
1145
1146
|
createGetRecentCommitsTool$1(),
|
|
1146
|
-
createGroupFilesByConcernTool$1()
|
|
1147
|
+
createGroupFilesByConcernTool$1(),
|
|
1148
|
+
createGetFileModificationTimesTool()
|
|
1147
1149
|
];
|
|
1148
1150
|
}
|
|
1149
1151
|
function createGetFileHistoryTool$1() {
|
|
@@ -1562,6 +1564,115 @@ Suggestion: These ${groupCount} groups might be better as separate commits if th
|
|
|
1562
1564
|
}
|
|
1563
1565
|
};
|
|
1564
1566
|
}
|
|
1567
|
+
function createGetFileModificationTimesTool() {
|
|
1568
|
+
return {
|
|
1569
|
+
name: "get_file_modification_times",
|
|
1570
|
+
description: "Get modification timestamps for changed files. Temporal proximity is one signal (among others) that can help identify related changes. Files modified close together MAY be part of the same logical change, but this is not guaranteed - always cross-reference with logical groupings.",
|
|
1571
|
+
category: "Organization",
|
|
1572
|
+
cost: "cheap",
|
|
1573
|
+
examples: [
|
|
1574
|
+
{
|
|
1575
|
+
scenario: "Identify files modified together vs at different times",
|
|
1576
|
+
params: { filePaths: ["src/auth.ts", "src/user.ts", "README.md", "tests/auth.test.ts"] },
|
|
1577
|
+
expectedResult: "Files with timestamps grouped by temporal proximity"
|
|
1578
|
+
}
|
|
1579
|
+
],
|
|
1580
|
+
parameters: {
|
|
1581
|
+
type: "object",
|
|
1582
|
+
properties: {
|
|
1583
|
+
filePaths: {
|
|
1584
|
+
type: "array",
|
|
1585
|
+
description: "All changed files to analyze",
|
|
1586
|
+
items: { type: "string", description: "File path" }
|
|
1587
|
+
}
|
|
1588
|
+
},
|
|
1589
|
+
required: ["filePaths"]
|
|
1590
|
+
},
|
|
1591
|
+
execute: async (params, context) => {
|
|
1592
|
+
const { filePaths } = params;
|
|
1593
|
+
const workingDir = context?.workingDirectory || process.cwd();
|
|
1594
|
+
const fileInfos = [];
|
|
1595
|
+
const errors = [];
|
|
1596
|
+
for (const filePath of filePaths) {
|
|
1597
|
+
try {
|
|
1598
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(workingDir, filePath);
|
|
1599
|
+
const stat = fs$1.statSync(fullPath);
|
|
1600
|
+
fileInfos.push({
|
|
1601
|
+
file: filePath,
|
|
1602
|
+
mtime: stat.mtime,
|
|
1603
|
+
mtimeMs: stat.mtimeMs
|
|
1604
|
+
});
|
|
1605
|
+
} catch {
|
|
1606
|
+
errors.push(filePath);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
fileInfos.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
1610
|
+
const CLUSTER_THRESHOLD_MS = 10 * 60 * 1e3;
|
|
1611
|
+
const clusters = [];
|
|
1612
|
+
let currentCluster = [];
|
|
1613
|
+
for (const info of fileInfos) {
|
|
1614
|
+
if (currentCluster.length === 0) {
|
|
1615
|
+
currentCluster.push(info);
|
|
1616
|
+
} else {
|
|
1617
|
+
const lastFile = currentCluster[currentCluster.length - 1];
|
|
1618
|
+
if (info.mtimeMs - lastFile.mtimeMs <= CLUSTER_THRESHOLD_MS) {
|
|
1619
|
+
currentCluster.push(info);
|
|
1620
|
+
} else {
|
|
1621
|
+
clusters.push(currentCluster);
|
|
1622
|
+
currentCluster = [info];
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
if (currentCluster.length > 0) {
|
|
1627
|
+
clusters.push(currentCluster);
|
|
1628
|
+
}
|
|
1629
|
+
let output = `## File Modification Times (sorted oldest to newest)
|
|
1630
|
+
|
|
1631
|
+
`;
|
|
1632
|
+
for (const info of fileInfos) {
|
|
1633
|
+
const timeStr = info.mtime.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
|
|
1634
|
+
output += `${timeStr} ${info.file}
|
|
1635
|
+
`;
|
|
1636
|
+
}
|
|
1637
|
+
if (errors.length > 0) {
|
|
1638
|
+
output += `
|
|
1639
|
+
## Files not found (possibly deleted):
|
|
1640
|
+
`;
|
|
1641
|
+
output += errors.map((f) => ` - ${f}`).join("\n");
|
|
1642
|
+
}
|
|
1643
|
+
output += `
|
|
1644
|
+
|
|
1645
|
+
## Temporal Clusters (files modified within 10 minutes of each other)
|
|
1646
|
+
|
|
1647
|
+
`;
|
|
1648
|
+
if (clusters.length === 1) {
|
|
1649
|
+
output += `All ${fileInfos.length} files were modified in a single work session.
|
|
1650
|
+
`;
|
|
1651
|
+
} else {
|
|
1652
|
+
output += `Found ${clusters.length} distinct work sessions:
|
|
1653
|
+
|
|
1654
|
+
`;
|
|
1655
|
+
clusters.forEach((cluster, idx) => {
|
|
1656
|
+
const startTime = cluster[0].mtime.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
|
|
1657
|
+
const endTime = cluster[cluster.length - 1].mtime.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
|
|
1658
|
+
const durationMs = cluster[cluster.length - 1].mtimeMs - cluster[0].mtimeMs;
|
|
1659
|
+
const durationMins = Math.round(durationMs / 6e4);
|
|
1660
|
+
output += `### Session ${idx + 1} (${cluster.length} files, ${durationMins} min span)
|
|
1661
|
+
`;
|
|
1662
|
+
output += `Time: ${startTime} to ${endTime}
|
|
1663
|
+
`;
|
|
1664
|
+
output += `Files:
|
|
1665
|
+
`;
|
|
1666
|
+
output += cluster.map((f) => ` - ${f.file}`).join("\n");
|
|
1667
|
+
output += "\n\n";
|
|
1668
|
+
});
|
|
1669
|
+
output += `
|
|
1670
|
+
**Note**: These ${clusters.length} temporal clusters may indicate separate work sessions. Cross-reference with logical groupings to determine if they represent distinct changes worth splitting.`;
|
|
1671
|
+
}
|
|
1672
|
+
return output;
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1565
1676
|
async function runAgenticCommit(config) {
|
|
1566
1677
|
const {
|
|
1567
1678
|
changedFiles,
|
|
@@ -1627,52 +1738,120 @@ async function runAgenticCommit(config) {
|
|
|
1627
1738
|
};
|
|
1628
1739
|
}
|
|
1629
1740
|
function buildSystemPrompt$2(toolGuidance) {
|
|
1630
|
-
return `You are
|
|
1741
|
+
return `You are a professional software engineer writing commit messages for your team.
|
|
1631
1742
|
|
|
1632
1743
|
${toolGuidance}
|
|
1633
1744
|
|
|
1745
|
+
## Your Task
|
|
1746
|
+
|
|
1747
|
+
Analyze the staged changes and determine the best way to commit them. Your primary goal is to create **meaningful, atomic commits** that each represent a single logical change.
|
|
1748
|
+
|
|
1749
|
+
**CRITICAL**: When there are many changed files, especially after a long work session (multiple hours), you should almost always split them into multiple commits. A single commit with 10+ files usually indicates multiple distinct changes that should be separated.
|
|
1750
|
+
|
|
1751
|
+
Think about:
|
|
1752
|
+
- What distinct features, fixes, or improvements are represented?
|
|
1753
|
+
- Are there natural groupings by functionality, module, or purpose?
|
|
1754
|
+
- When were files modified relative to each other?
|
|
1755
|
+
- What should reviewers focus on in each commit?
|
|
1756
|
+
|
|
1634
1757
|
## Investigation Strategy
|
|
1635
1758
|
|
|
1636
|
-
For
|
|
1637
|
-
|
|
1759
|
+
For any non-trivial set of changes, gather multiple signals to understand how to group commits:
|
|
1760
|
+
|
|
1761
|
+
1. **Check file modification times** using \`get_file_modification_times\`
|
|
1762
|
+
- This reveals *when* files were changed relative to each other
|
|
1763
|
+
- Files modified close together *may* be part of the same logical change
|
|
1764
|
+
- Large temporal gaps *can* indicate separate work sessions
|
|
1765
|
+
- Note: temporal proximity is one signal, not a guarantee of relatedness
|
|
1766
|
+
|
|
1767
|
+
2. **Understand logical relationships** using \`group_files_by_concern\`
|
|
1768
|
+
- Groups by module, type (tests, docs, source), and directory structure
|
|
1769
|
+
- Reveals which files are functionally related regardless of when they were modified
|
|
1770
|
+
|
|
1771
|
+
3. **Cross-reference both signals** to make informed decisions:
|
|
1772
|
+
- Files modified together AND logically related → strong candidate for same commit
|
|
1773
|
+
- Files modified apart BUT logically related → might still belong together
|
|
1774
|
+
- Files modified together BUT logically unrelated → consider splitting
|
|
1775
|
+
- Use your judgment - neither signal is definitive on its own
|
|
1776
|
+
|
|
1777
|
+
4. **Investigate further** as needed:
|
|
1778
|
+
- get_file_content - see full context when diffs are unclear
|
|
1779
|
+
- get_file_history - understand how code evolved
|
|
1780
|
+
- get_file_dependencies - assess impact of changes
|
|
1781
|
+
- get_recent_commits - avoid duplicate messages
|
|
1782
|
+
- get_related_tests - understand behavior changes
|
|
1783
|
+
- search_codebase - find usage patterns
|
|
1784
|
+
|
|
1785
|
+
## When to Split Commits
|
|
1786
|
+
|
|
1787
|
+
**Prefer multiple commits when:**
|
|
1788
|
+
- Changes span multiple hours of work (check modification times!)
|
|
1789
|
+
- Different logical features or fixes are included
|
|
1790
|
+
- Test changes could be separated from production code changes
|
|
1791
|
+
- Documentation updates are unrelated to code changes
|
|
1792
|
+
- Refactoring is mixed with feature work
|
|
1793
|
+
- Configuration changes are mixed with implementation changes
|
|
1794
|
+
- Files in different modules/packages are changed for different reasons
|
|
1795
|
+
|
|
1796
|
+
**Keep as one commit when:**
|
|
1797
|
+
- All changes are part of a single, cohesive feature
|
|
1798
|
+
- Files were modified together in a focused work session
|
|
1799
|
+
- Test changes directly test the production code changes
|
|
1800
|
+
- The changes tell a single coherent story
|
|
1801
|
+
|
|
1802
|
+
**Default bias**: When in doubt about whether to split, **prefer splitting**. It's better to have too many focused commits than too few bloated ones. A good commit should be understandable and reviewable in isolation.
|
|
1803
|
+
|
|
1804
|
+
## Important Context Guidelines
|
|
1805
|
+
|
|
1806
|
+
If additional context is provided (from context files or other sources), use your judgment:
|
|
1807
|
+
- If the context is relevant to these specific changes, incorporate it
|
|
1808
|
+
- If the context describes unrelated changes or other packages, ignore it
|
|
1809
|
+
- Don't force connections between unrelated information
|
|
1810
|
+
- Focus on accurately describing what actually changed
|
|
1638
1811
|
|
|
1639
|
-
|
|
1640
|
-
- Use 2-4 tools: group_files_by_concern, get_file_content for key files, get_recent_commits, get_related_tests
|
|
1812
|
+
## Writing Style
|
|
1641
1813
|
|
|
1642
|
-
|
|
1643
|
-
- Use
|
|
1814
|
+
Write naturally and directly:
|
|
1815
|
+
- Use plain language, not corporate speak
|
|
1816
|
+
- Be specific and concrete
|
|
1817
|
+
- Avoid buzzwords and jargon
|
|
1818
|
+
- No emojis or excessive punctuation
|
|
1819
|
+
- No phrases like "this commit" or "this PR"
|
|
1820
|
+
- No meta-commentary about the commit itself
|
|
1644
1821
|
|
|
1645
|
-
|
|
1822
|
+
Follow conventional commit format when appropriate (feat:, fix:, refactor:, docs:, test:, chore:), but prioritize clarity over formality.
|
|
1646
1823
|
|
|
1647
|
-
##
|
|
1648
|
-
- Follow conventional commit format when appropriate (feat:, fix:, refactor:, docs:, test:, chore:)
|
|
1649
|
-
- Consider suggesting split commits for unrelated changes
|
|
1650
|
-
- Output only the commit message and/or splits - no conversational remarks
|
|
1651
|
-
- NEVER include phrases like "If you want" or "Let me know" or offer follow-up actions
|
|
1824
|
+
## Output Format
|
|
1652
1825
|
|
|
1653
|
-
|
|
1654
|
-
When you're ready to provide the final commit message, format it as:
|
|
1826
|
+
When ready, format your response as:
|
|
1655
1827
|
|
|
1656
1828
|
COMMIT_MESSAGE:
|
|
1657
|
-
[Your
|
|
1829
|
+
[Your commit message here - only for the FIRST group of changes if splitting]
|
|
1658
1830
|
|
|
1659
|
-
If
|
|
1831
|
+
If changes should be split into multiple commits (which is often the case with large changesets):
|
|
1660
1832
|
|
|
1661
1833
|
SUGGESTED_SPLITS:
|
|
1662
1834
|
Split 1:
|
|
1663
1835
|
Files: [list of files]
|
|
1664
|
-
Rationale: [why these belong together]
|
|
1836
|
+
Rationale: [why these belong together - mention temporal clustering and/or logical relationship]
|
|
1665
1837
|
Message: [commit message for this split]
|
|
1666
1838
|
|
|
1667
1839
|
Split 2:
|
|
1840
|
+
Files: [list of files]
|
|
1841
|
+
Rationale: [why these belong together]
|
|
1842
|
+
Message: [commit message for this split]
|
|
1843
|
+
|
|
1844
|
+
Split 3:
|
|
1668
1845
|
...
|
|
1669
1846
|
|
|
1670
|
-
|
|
1847
|
+
Output only the commit message and splits. No conversational remarks or follow-up offers.`;
|
|
1671
1848
|
}
|
|
1672
1849
|
function buildUserMessage$1(changedFiles, diffContent, userDirection, logContext) {
|
|
1850
|
+
const fileCount = changedFiles.length;
|
|
1851
|
+
const manyFiles = fileCount >= 5;
|
|
1673
1852
|
let message = `I have staged changes that need a commit message.
|
|
1674
1853
|
|
|
1675
|
-
Changed files (${
|
|
1854
|
+
Changed files (${fileCount}):
|
|
1676
1855
|
${changedFiles.map((f) => ` - ${f}`).join("\n")}
|
|
1677
1856
|
|
|
1678
1857
|
Diff:
|
|
@@ -1690,9 +1869,24 @@ ${logContext}`;
|
|
|
1690
1869
|
}
|
|
1691
1870
|
message += `
|
|
1692
1871
|
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1872
|
+
## Your Analysis Task
|
|
1873
|
+
|
|
1874
|
+
${manyFiles ? `With ${fileCount} files changed, consider whether these represent multiple distinct changes that should be split into separate commits.
|
|
1875
|
+
|
|
1876
|
+
` : ""}Gather signals to understand the changes:
|
|
1877
|
+
1. Use \`get_file_modification_times\` to see when files were modified relative to each other
|
|
1878
|
+
2. Use \`group_files_by_concern\` to understand how files relate by type/purpose
|
|
1879
|
+
3. Cross-reference both signals - temporal proximity and logical relatedness - to determine the best commit structure
|
|
1880
|
+
|
|
1881
|
+
Consider:
|
|
1882
|
+
- What distinct features, fixes, or improvements are included?
|
|
1883
|
+
- Are files that were modified together also logically related?
|
|
1884
|
+
- Would separate commits make the history more understandable?
|
|
1885
|
+
|
|
1886
|
+
${manyFiles ? "With many files, consider whether multiple focused commits would be clearer than one large commit." : "If changes represent multiple logical concerns, suggest splits."}
|
|
1887
|
+
|
|
1888
|
+
If context information is provided, use it only if relevant to these specific changes.
|
|
1889
|
+
Don't force connections that don't exist - focus on what actually changed.`;
|
|
1696
1890
|
return message;
|
|
1697
1891
|
}
|
|
1698
1892
|
function parseAgenticResult$1(finalMessage) {
|
|
@@ -2455,56 +2649,79 @@ async function runAgenticRelease(config) {
|
|
|
2455
2649
|
};
|
|
2456
2650
|
}
|
|
2457
2651
|
function buildSystemPrompt$1(toolGuidance) {
|
|
2458
|
-
return `You are
|
|
2652
|
+
return `You are a professional software engineer writing release notes for your team and users.
|
|
2459
2653
|
|
|
2460
2654
|
${toolGuidance}
|
|
2461
|
-
- get_tag_history: Use early to understand release cadence. Good for: establishing context about project versioning patterns
|
|
2462
2655
|
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
- get_file_dependencies: Use for refactors/moves. Good for: assessing impact scope, identifying what depends on changed code
|
|
2467
|
-
- search_codebase: Use to find usage patterns. Good for: checking if APIs are widely used, finding similar patterns elsewhere
|
|
2656
|
+
## Your Task
|
|
2657
|
+
|
|
2658
|
+
Write release notes that clearly explain what's in this release and why it matters. Your notes should help users and developers understand what changed without needing to read every commit.
|
|
2468
2659
|
|
|
2469
|
-
|
|
2470
|
-
-
|
|
2471
|
-
-
|
|
2472
|
-
-
|
|
2660
|
+
Focus on:
|
|
2661
|
+
- What problems does this release solve?
|
|
2662
|
+
- What new capabilities does it add?
|
|
2663
|
+
- What's the impact on users and developers?
|
|
2664
|
+
- Are there breaking changes or important considerations?
|
|
2473
2665
|
|
|
2474
|
-
|
|
2475
|
-
- get_breaking_changes: Always use. Good for: identifying API changes, finding removals/signature changes that could break users
|
|
2476
|
-
- get_related_tests: Use for significant logic changes. Good for: understanding what behavior changed, verifying test coverage exists
|
|
2666
|
+
Use the available tools to investigate the changes. The more you understand, the better your notes will be.
|
|
2477
2667
|
|
|
2478
|
-
|
|
2668
|
+
**Important**: If additional context is provided (from context files or other sources), use your judgment:
|
|
2669
|
+
- If the context is relevant to this specific package/release, incorporate it appropriately
|
|
2670
|
+
- If the context describes changes in other packages or unrelated work, ignore it
|
|
2671
|
+
- Don't fabricate connections between this package and unrelated context
|
|
2672
|
+
- Be honest about what changed - only mention what actually happened in this release
|
|
2673
|
+
- Context is supplemental information, not a requirement to include
|
|
2479
2674
|
|
|
2480
|
-
|
|
2481
|
-
2. **Identify themes** (1-2 tools): group_files_by_concern if many files, compare_previous_release for context
|
|
2482
|
-
3. **Deep dive** (3-5 tools): get_file_content for key changes, get_file_dependencies for refactors, analyze_diff_section for unclear changes
|
|
2483
|
-
4. **Verify understanding** (2-3 tools): get_related_tests for logic changes, search_codebase for impact
|
|
2484
|
-
5. **Check risks** (1 tool): get_breaking_changes always
|
|
2675
|
+
## Investigation Approach
|
|
2485
2676
|
|
|
2486
|
-
Use
|
|
2677
|
+
Use tools based on what you need to know:
|
|
2487
2678
|
|
|
2488
|
-
|
|
2489
|
-
|
|
2679
|
+
**Context:**
|
|
2680
|
+
- get_tag_history - understand release patterns
|
|
2681
|
+
- get_release_stats - quantify scope
|
|
2682
|
+
- compare_previous_release - see how this compares
|
|
2683
|
+
|
|
2684
|
+
**Understanding:**
|
|
2685
|
+
- group_files_by_concern - identify themes
|
|
2686
|
+
- analyze_commit_patterns - detect patterns
|
|
2687
|
+
- get_file_content - see full context
|
|
2688
|
+
- analyze_diff_section - expand unclear changes
|
|
2689
|
+
- get_file_history - understand evolution
|
|
2690
|
+
|
|
2691
|
+
**Impact:**
|
|
2692
|
+
- get_file_dependencies - assess reach
|
|
2693
|
+
- search_codebase - find usage patterns
|
|
2694
|
+
- get_related_tests - understand behavior changes
|
|
2695
|
+
- get_breaking_changes - identify breaking changes (always use)
|
|
2696
|
+
|
|
2697
|
+
## Writing Style
|
|
2698
|
+
|
|
2699
|
+
Write naturally and directly:
|
|
2700
|
+
- Use plain language that users can understand
|
|
2701
|
+
- Be specific about what changed and why
|
|
2702
|
+
- Avoid marketing speak and buzzwords
|
|
2703
|
+
- No emojis or excessive punctuation
|
|
2704
|
+
- No phrases like "we're excited to announce"
|
|
2705
|
+
- No meta-commentary about the release itself
|
|
2706
|
+
- Focus on facts and implications, not enthusiasm
|
|
2707
|
+
|
|
2708
|
+
Structure your notes logically:
|
|
2709
|
+
- Start with the most important changes
|
|
2710
|
+
- Group related changes together
|
|
2711
|
+
- Explain breaking changes clearly
|
|
2712
|
+
- Include practical examples when helpful
|
|
2713
|
+
|
|
2714
|
+
## Output Format
|
|
2715
|
+
|
|
2716
|
+
When ready, format your response as JSON:
|
|
2490
2717
|
|
|
2491
2718
|
RELEASE_NOTES:
|
|
2492
2719
|
{
|
|
2493
|
-
"title": "
|
|
2494
|
-
"body": "
|
|
2495
|
-
}
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
- Demonstrate genuine understanding of the changes
|
|
2499
|
-
- Provide context and explain implications
|
|
2500
|
-
- Connect related changes to reveal patterns
|
|
2501
|
-
- Be substantial and analytical, not formulaic
|
|
2502
|
-
- Sound like they were written by a human who studied the changes
|
|
2503
|
-
- Be grounded in actual commits and issues (no hallucinations)
|
|
2504
|
-
- Be standalone documentation that can be published as-is
|
|
2505
|
-
- NEVER include conversational elements like "If you want, I can also..." or "Let me know if..."
|
|
2506
|
-
- NEVER offer follow-up actions, questions, or suggestions for additional work
|
|
2507
|
-
- End with substantive content, not conversational closing remarks`;
|
|
2720
|
+
"title": "Clear, factual title describing the main changes",
|
|
2721
|
+
"body": "Detailed release notes in Markdown format"
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
Output only the JSON. No conversational remarks or follow-up offers.`;
|
|
2508
2725
|
}
|
|
2509
2726
|
function buildUserMessage(params) {
|
|
2510
2727
|
const { fromRef, toRef, logContent, diffContent, milestoneIssues, releaseFocus, userContext } = params;
|
|
@@ -2537,15 +2754,17 @@ ${userContext}`;
|
|
|
2537
2754
|
}
|
|
2538
2755
|
message += `
|
|
2539
2756
|
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2757
|
+
Analyze these changes and write clear release notes. Consider:
|
|
2758
|
+
- What's the main story of this release?
|
|
2759
|
+
- What problems does it solve?
|
|
2760
|
+
- What's the impact on users and developers?
|
|
2761
|
+
- Are there breaking changes?
|
|
2762
|
+
|
|
2763
|
+
If context information is provided, use it only if relevant to this specific package.
|
|
2764
|
+
Don't force connections that don't exist - if context describes changes in other packages
|
|
2765
|
+
or unrelated features, simply ignore it and focus on what actually changed in this release.
|
|
2547
2766
|
|
|
2548
|
-
|
|
2767
|
+
Investigate as needed to write accurate, helpful release notes.`;
|
|
2549
2768
|
return message;
|
|
2550
2769
|
}
|
|
2551
2770
|
function parseAgenticResult(finalMessage) {
|