@eldrforge/ai-service 0.1.14 → 0.1.15

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.
Files changed (33) hide show
  1. package/dist/index.js +1266 -78
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/agentic/commit.d.ts +6 -0
  4. package/dist/src/agentic/commit.d.ts.map +1 -1
  5. package/dist/src/agentic/executor.d.ts +7 -1
  6. package/dist/src/agentic/executor.d.ts.map +1 -1
  7. package/dist/src/agentic/publish.d.ts +31 -0
  8. package/dist/src/agentic/publish.d.ts.map +1 -0
  9. package/dist/src/agentic/release.d.ts +6 -0
  10. package/dist/src/agentic/release.d.ts.map +1 -1
  11. package/dist/src/ai.d.ts.map +1 -1
  12. package/dist/src/index.d.ts +3 -0
  13. package/dist/src/index.d.ts.map +1 -1
  14. package/dist/src/observability/conversation-logger.d.ts +53 -0
  15. package/dist/src/observability/conversation-logger.d.ts.map +1 -0
  16. package/dist/src/observability/index.d.ts +15 -0
  17. package/dist/src/observability/index.d.ts.map +1 -0
  18. package/dist/src/observability/metrics.d.ts +53 -0
  19. package/dist/src/observability/metrics.d.ts.map +1 -0
  20. package/dist/src/observability/reflection.d.ts +36 -0
  21. package/dist/src/observability/reflection.d.ts.map +1 -0
  22. package/dist/src/prompts/commit.d.ts.map +1 -1
  23. package/dist/src/prompts/index.d.ts +1 -0
  24. package/dist/src/prompts/index.d.ts.map +1 -1
  25. package/dist/src/prompts/release.d.ts.map +1 -1
  26. package/dist/src/prompts/review.d.ts.map +1 -1
  27. package/dist/src/prompts/templates.d.ts +22 -0
  28. package/dist/src/prompts/templates.d.ts.map +1 -0
  29. package/dist/src/tools/publish-tools.d.ts +6 -0
  30. package/dist/src/tools/publish-tools.d.ts.map +1 -0
  31. package/dist/src/tools/types.d.ts +17 -0
  32. package/dist/src/tools/types.d.ts.map +1 -1
  33. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { OpenAI } from "openai";
2
- import { safeJsonParse, run } from "@eldrforge/git-tools";
2
+ import { safeJsonParse, run, localBranchExists, isBranchInSyncWithRemote, safeSyncBranchWithRemote } from "@eldrforge/git-tools";
3
3
  import fs$1 from "fs";
4
4
  import { spawnSync } from "child_process";
5
5
  import * as path from "path";
6
6
  import path__default from "path";
7
7
  import * as os from "os";
8
8
  import * as fs from "fs/promises";
9
- import { recipe } from "@riotprompt/riotprompt";
9
+ import { registerTemplates, cook, ConversationBuilder, generateToolGuidance, ConversationLogger, MetricsCollector } from "@riotprompt/riotprompt";
10
10
  import { fileURLToPath } from "url";
11
11
  let logger;
12
12
  function setLogger(customLogger) {
@@ -114,7 +114,10 @@ async function createCompletion(messages, options = { model: "gpt-4o-mini" }) {
114
114
  if (!apiKey) {
115
115
  throw new OpenAIError("OPENAI_API_KEY environment variable is not set");
116
116
  }
117
- const timeoutMs = parseInt(process.env.OPENAI_TIMEOUT_MS || "300000");
117
+ const timeoutMs = parseInt(process.env.OPENAI_TIMEOUT_MS || "300000", 10);
118
+ if (isNaN(timeoutMs) || timeoutMs <= 0) {
119
+ throw new OpenAIError("Invalid OPENAI_TIMEOUT_MS value - must be a positive number");
120
+ }
118
121
  openai = new OpenAI({
119
122
  apiKey,
120
123
  timeout: timeoutMs
@@ -157,8 +160,9 @@ async function createCompletion(messages, options = { model: "gpt-4o-mini" }) {
157
160
  const completionPromise = openai.chat.completions.create(apiOptions);
158
161
  let timeoutId = null;
159
162
  const timeoutPromise = new Promise((_, reject) => {
160
- const timeoutMs2 = parseInt(process.env.OPENAI_TIMEOUT_MS || "300000");
161
- timeoutId = setTimeout(() => reject(new OpenAIError(`OpenAI API call timed out after ${timeoutMs2 / 1e3} seconds`)), timeoutMs2);
163
+ const timeoutMs2 = parseInt(process.env.OPENAI_TIMEOUT_MS || "300000", 10);
164
+ const validTimeout = isNaN(timeoutMs2) || timeoutMs2 <= 0 ? 3e5 : timeoutMs2;
165
+ timeoutId = setTimeout(() => reject(new OpenAIError(`OpenAI API call timed out after ${validTimeout / 1e3} seconds`)), validTimeout);
162
166
  });
163
167
  let progressIntervalId = null;
164
168
  progressIntervalId = setInterval(() => {
@@ -710,6 +714,31 @@ function requireTTY(errorMessage = "Interactive mode requires a terminal. Use --
710
714
  throw new Error(errorMessage);
711
715
  }
712
716
  }
717
+ const initializeTemplates = () => {
718
+ const templates = {
719
+ // Commit message generation template
720
+ "commit": {
721
+ persona: { path: "personas/you.md" },
722
+ instructions: [{ path: "instructions/commit.md" }]
723
+ },
724
+ // Release notes generation template
725
+ "release": {
726
+ persona: { path: "personas/releaser.md" },
727
+ instructions: [{ path: "instructions/release.md" }]
728
+ },
729
+ // Code review template
730
+ "review": {
731
+ persona: { path: "personas/you.md" },
732
+ instructions: [{ path: "instructions/review.md" }]
733
+ }
734
+ };
735
+ registerTemplates(templates);
736
+ };
737
+ const TemplateNames = {
738
+ COMMIT: "commit",
739
+ RELEASE: "release",
740
+ REVIEW: "review"
741
+ };
713
742
  const __filename$3 = fileURLToPath(import.meta.url);
714
743
  const __dirname$3 = path__default.dirname(__filename$3);
715
744
  const createCommitPrompt = async ({ overridePaths: _overridePaths, overrides: _overrides }, { diffContent, userDirection, isFileContent, githubIssuesContext }, { logContext, context, directories } = {}) => {
@@ -735,7 +764,14 @@ const createCommitPrompt = async ({ overridePaths: _overridePaths, overrides: _o
735
764
  if (directories && directories.length > 0) {
736
765
  contextItems.push({ directories, title: "Directories" });
737
766
  }
738
- return recipe(basePath).persona({ path: "personas/you.md" }).instructions({ path: "instructions/commit.md" }).overridePaths(_overridePaths ?? []).overrides(_overrides ?? true).content(...contentItems).context(...contextItems).cook();
767
+ return cook({
768
+ basePath,
769
+ template: TemplateNames.COMMIT,
770
+ overridePaths: _overridePaths ?? [],
771
+ overrides: _overrides ?? true,
772
+ content: contentItems,
773
+ context: contextItems
774
+ });
739
775
  };
740
776
  const __filename$2 = fileURLToPath(import.meta.url);
741
777
  const __dirname$2 = path__default.dirname(__filename$2);
@@ -780,7 +816,14 @@ const createReleasePrompt = async ({ overrides: _overrides, overridePaths: _over
780
816
  if (directories && directories.length > 0) {
781
817
  contextItems.push({ directories, title: "Directories" });
782
818
  }
783
- const prompt = await recipe(basePath).persona({ path: "personas/releaser.md" }).instructions({ path: "instructions/release.md" }).overridePaths(_overridePaths ?? []).overrides(_overrides ?? true).content(...contentItems).context(...contextItems).cook();
819
+ const prompt = await cook({
820
+ basePath,
821
+ template: TemplateNames.RELEASE,
822
+ overridePaths: _overridePaths ?? [],
823
+ overrides: _overrides ?? true,
824
+ content: contentItems,
825
+ context: contextItems
826
+ });
784
827
  return {
785
828
  prompt,
786
829
  maxTokens,
@@ -814,7 +857,14 @@ const createReviewPrompt = async ({ overridePaths: _overridePaths, overrides: _o
814
857
  if (directories && directories.length > 0) {
815
858
  contextItems.push({ directories, title: "Directories" });
816
859
  }
817
- return recipe(basePath).persona({ path: "personas/you.md" }).instructions({ path: "instructions/review.md" }).overridePaths(_overridePaths ?? []).overrides(_overrides ?? true).content(...contentItems).context(...contextItems).cook();
860
+ return cook({
861
+ basePath,
862
+ template: TemplateNames.REVIEW,
863
+ overridePaths: _overridePaths ?? [],
864
+ overrides: _overrides ?? true,
865
+ content: contentItems,
866
+ context: contextItems
867
+ });
818
868
  };
819
869
  class AgenticExecutor {
820
870
  logger;
@@ -823,7 +873,7 @@ class AgenticExecutor {
823
873
  this.logger = logger2;
824
874
  }
825
875
  /**
826
- * Run the agentic loop
876
+ * Run the agentic loop with ConversationBuilder
827
877
  */
828
878
  async run(config) {
829
879
  const {
@@ -836,15 +886,33 @@ class AgenticExecutor {
836
886
  debugResponseFile,
837
887
  storage,
838
888
  logger: logger2,
839
- openaiReasoning = false
889
+ openaiReasoning = false,
890
+ tokenBudget
840
891
  } = config;
841
- const messages = [...initialMessages];
892
+ const conversation = ConversationBuilder.create({ model }, logger2);
893
+ for (const msg of initialMessages) {
894
+ if (msg.role === "system") {
895
+ conversation.addSystemMessage(msg.content);
896
+ } else if (msg.role === "user") {
897
+ conversation.addUserMessage(msg.content);
898
+ }
899
+ }
900
+ if (tokenBudget) {
901
+ this.log("Configuring token budget", tokenBudget);
902
+ conversation.withTokenBudget({
903
+ max: tokenBudget.max,
904
+ reserveForResponse: tokenBudget.reserveForResponse || 4e3,
905
+ strategy: tokenBudget.strategy || "fifo",
906
+ onBudgetExceeded: tokenBudget.onBudgetExceeded || "compress"
907
+ });
908
+ }
842
909
  let iterations = 0;
843
910
  let toolCallsExecuted = 0;
844
- this.log("Starting agentic loop", { maxIterations, toolCount: tools.count() });
911
+ this.log("Starting agentic loop with ConversationBuilder", { maxIterations, toolCount: tools.count() });
845
912
  while (iterations < maxIterations) {
846
913
  iterations++;
847
914
  this.log(`Iteration ${iterations}/${maxIterations}`);
915
+ const messages = conversation.toMessages();
848
916
  const response = await createCompletionWithRetry(
849
917
  messages,
850
918
  {
@@ -863,23 +931,16 @@ class AgenticExecutor {
863
931
  if (toolCalls.length === 0) {
864
932
  const finalContent = message.content || "";
865
933
  this.log("Agent completed without tool calls", { iterations, toolCallsExecuted });
866
- messages.push({
867
- role: "assistant",
868
- content: finalContent
869
- });
934
+ conversation.addAssistantMessage(finalContent);
870
935
  return {
871
936
  finalMessage: finalContent,
872
937
  iterations,
873
938
  toolCallsExecuted,
874
- conversationHistory: messages,
939
+ conversationHistory: conversation.toMessages(),
875
940
  toolMetrics: this.toolMetrics
876
941
  };
877
942
  }
878
- messages.push({
879
- role: "assistant",
880
- content: message.content || null,
881
- tool_calls: toolCalls
882
- });
943
+ conversation.addAssistantWithToolCalls(message.content || null, toolCalls);
883
944
  this.log(`Executing ${toolCalls.length} tool call(s)`);
884
945
  for (const toolCall of toolCalls) {
885
946
  const startTime = Date.now();
@@ -889,14 +950,16 @@ class AgenticExecutor {
889
950
  if (this.logger?.info) {
890
951
  this.logger.info(`🔧 Running tool: ${toolName}`);
891
952
  }
892
- const args = JSON.parse(toolCall.function.arguments);
953
+ let args;
954
+ try {
955
+ args = JSON.parse(toolCall.function.arguments);
956
+ } catch (parseError) {
957
+ throw new Error(`Failed to parse tool arguments: ${parseError.message}`);
958
+ }
893
959
  const result = await tools.execute(toolName, args);
894
960
  const duration = Date.now() - startTime;
895
- messages.push({
896
- role: "tool",
897
- tool_call_id: toolCall.id,
898
- content: this.formatToolResult({ id: toolCall.id, name: toolName, result })
899
- });
961
+ const formattedResult = this.formatToolResult({ id: toolCall.id, name: toolName, result });
962
+ conversation.addToolResult(toolCall.id, formattedResult, toolName);
900
963
  toolCallsExecuted++;
901
964
  this.toolMetrics.push({
902
965
  name: toolName,
@@ -924,22 +987,16 @@ class AgenticExecutor {
924
987
  if (this.logger?.warn) {
925
988
  this.logger.warn(`❌ Tool ${toolName} failed: ${errorMessage}`);
926
989
  }
927
- messages.push({
928
- role: "tool",
929
- tool_call_id: toolCall.id,
930
- content: `Tool execution failed: ${errorMessage}`
931
- });
990
+ conversation.addToolResult(toolCall.id, `Tool execution failed: ${errorMessage}`, toolName);
932
991
  }
933
992
  }
934
993
  this.log(`Completed tool execution`, { toolCallsExecuted });
935
994
  }
936
995
  this.log("Max iterations reached, forcing completion", { iterations, toolCallsExecuted });
937
- messages.push({
938
- role: "user",
939
- content: "Please provide your final analysis and commit message based on your investigation. Do not request any more tools."
940
- });
996
+ conversation.addUserMessage("Please provide your final analysis based on your investigation. Do not request any more tools.");
997
+ const finalMessages = conversation.toMessages();
941
998
  const finalResponse = await createCompletionWithRetry(
942
- messages,
999
+ finalMessages,
943
1000
  {
944
1001
  model,
945
1002
  openaiReasoning: openaiReasoning || void 0,
@@ -948,15 +1005,12 @@ class AgenticExecutor {
948
1005
  logger: logger2
949
1006
  }
950
1007
  );
951
- messages.push({
952
- role: "assistant",
953
- content: finalResponse
954
- });
1008
+ conversation.addAssistantMessage(finalResponse);
955
1009
  return {
956
1010
  finalMessage: finalResponse,
957
1011
  iterations,
958
1012
  toolCallsExecuted,
959
- conversationHistory: messages,
1013
+ conversationHistory: conversation.toMessages(),
960
1014
  toolMetrics: this.toolMetrics
961
1015
  };
962
1016
  }
@@ -1096,6 +1150,20 @@ function createGetFileHistoryTool$1() {
1096
1150
  return {
1097
1151
  name: "get_file_history",
1098
1152
  description: "Get git commit history for one or more files to understand their evolution and past changes",
1153
+ category: "Understanding",
1154
+ cost: "cheap",
1155
+ examples: [
1156
+ {
1157
+ scenario: "Check if file has recent refactoring",
1158
+ params: { filePaths: ["src/auth.ts"], limit: 5 },
1159
+ expectedResult: "List of recent commits affecting auth.ts"
1160
+ },
1161
+ {
1162
+ scenario: "Understand evolution of multiple related files",
1163
+ params: { filePaths: ["src/user.ts", "src/auth.ts"], limit: 10, format: "detailed" },
1164
+ expectedResult: "Detailed commit history for both files"
1165
+ }
1166
+ ],
1099
1167
  parameters: {
1100
1168
  type: "object",
1101
1169
  properties: {
@@ -1137,6 +1205,20 @@ function createGetFileContentTool$1() {
1137
1205
  return {
1138
1206
  name: "get_file_content",
1139
1207
  description: "Get the complete current content of a file to understand context around changes. Returns a message if the file does not exist (e.g., if it was deleted).",
1208
+ category: "Understanding",
1209
+ cost: "moderate",
1210
+ examples: [
1211
+ {
1212
+ scenario: "See full class definition for modified method",
1213
+ params: { filePath: "src/services/auth.ts", includeLineNumbers: true },
1214
+ expectedResult: "Complete file content with line numbers"
1215
+ },
1216
+ {
1217
+ scenario: "Check imports and structure",
1218
+ params: { filePath: "src/utils/helpers.ts" },
1219
+ expectedResult: "Full file content showing imports and exports"
1220
+ }
1221
+ ],
1140
1222
  parameters: {
1141
1223
  type: "object",
1142
1224
  properties: {
@@ -1180,6 +1262,20 @@ function createSearchCodebaseTool$1() {
1180
1262
  return {
1181
1263
  name: "search_codebase",
1182
1264
  description: "Search for code patterns, function names, or text across the codebase using git grep",
1265
+ category: "Analysis",
1266
+ cost: "moderate",
1267
+ examples: [
1268
+ {
1269
+ scenario: "Find all uses of a renamed function",
1270
+ params: { query: "oldFunctionName", fileTypes: ["ts", "tsx"] },
1271
+ expectedResult: "List of files and lines containing the function"
1272
+ },
1273
+ {
1274
+ scenario: "Check if pattern exists elsewhere",
1275
+ params: { query: "pattern.*string", contextLines: 3 },
1276
+ expectedResult: "Matching lines with surrounding context"
1277
+ }
1278
+ ],
1183
1279
  parameters: {
1184
1280
  type: "object",
1185
1281
  properties: {
@@ -1224,6 +1320,9 @@ function createGetRelatedTestsTool$1() {
1224
1320
  return {
1225
1321
  name: "get_related_tests",
1226
1322
  description: "Find test files related to production files to understand what the code is supposed to do",
1323
+ category: "Understanding",
1324
+ cost: "cheap",
1325
+ examples: [{ scenario: "Find tests for modified service", params: { filePaths: ["src/services/auth.ts"] }, expectedResult: "List of related test files" }],
1227
1326
  parameters: {
1228
1327
  type: "object",
1229
1328
  properties: {
@@ -1270,6 +1369,9 @@ function createGetFileDependenciesTool$1() {
1270
1369
  return {
1271
1370
  name: "get_file_dependencies",
1272
1371
  description: "Find which files import or depend on the changed files to assess change impact",
1372
+ category: "Analysis",
1373
+ cost: "moderate",
1374
+ examples: [{ scenario: "Check impact of util function change", params: { filePaths: ["src/utils/format.ts"] }, expectedResult: "Files that import format.ts" }],
1273
1375
  parameters: {
1274
1376
  type: "object",
1275
1377
  properties: {
@@ -1313,6 +1415,9 @@ function createAnalyzeDiffSectionTool$1() {
1313
1415
  return {
1314
1416
  name: "analyze_diff_section",
1315
1417
  description: "Get expanded context around specific lines in a file to better understand changes",
1418
+ category: "Understanding",
1419
+ cost: "cheap",
1420
+ examples: [{ scenario: "See context around confusing change", params: { filePath: "src/auth.ts", startLine: 45, endLine: 52, contextLines: 15 }, expectedResult: "Expanded code section with context" }],
1316
1421
  parameters: {
1317
1422
  type: "object",
1318
1423
  properties: {
@@ -1361,6 +1466,9 @@ function createGetRecentCommitsTool$1() {
1361
1466
  return {
1362
1467
  name: "get_recent_commits",
1363
1468
  description: "Get recent commits that modified the same files to understand recent work in this area",
1469
+ category: "Understanding",
1470
+ cost: "cheap",
1471
+ examples: [{ scenario: "Check for duplicate commit messages", params: { filePaths: ["src/auth.ts"], since: "1 week ago", limit: 5 }, expectedResult: "Recent commits to auth.ts" }],
1364
1472
  parameters: {
1365
1473
  type: "object",
1366
1474
  properties: {
@@ -1400,6 +1508,15 @@ function createGroupFilesByConcernTool$1() {
1400
1508
  return {
1401
1509
  name: "group_files_by_concern",
1402
1510
  description: "Analyze changed files and suggest logical groupings that might represent separate commits",
1511
+ category: "Organization",
1512
+ cost: "cheap",
1513
+ examples: [
1514
+ {
1515
+ scenario: "Check if multiple unrelated changes should be split",
1516
+ params: { filePaths: ["src/auth.ts", "README.md", "tests/auth.test.ts", "package.json"] },
1517
+ expectedResult: "Files grouped by concern with split suggestions"
1518
+ }
1519
+ ],
1403
1520
  parameters: {
1404
1521
  type: "object",
1405
1522
  properties: {
@@ -1458,7 +1575,8 @@ async function runAgenticCommit(config) {
1458
1575
  debugResponseFile,
1459
1576
  storage,
1460
1577
  logger: logger2,
1461
- openaiReasoning
1578
+ openaiReasoning,
1579
+ tokenBudget
1462
1580
  } = config;
1463
1581
  const toolRegistry = createToolRegistry({
1464
1582
  workingDirectory: process.cwd(),
@@ -1467,7 +1585,13 @@ async function runAgenticCommit(config) {
1467
1585
  });
1468
1586
  const tools = createCommitTools();
1469
1587
  toolRegistry.registerAll(tools);
1470
- const systemPrompt = buildSystemPrompt$1();
1588
+ const toolGuidance = generateToolGuidance(tools, {
1589
+ strategy: "adaptive",
1590
+ includeExamples: true,
1591
+ explainWhenToUse: true,
1592
+ includeCategories: true
1593
+ });
1594
+ const systemPrompt = buildSystemPrompt$2(toolGuidance);
1471
1595
  const userMessage = buildUserMessage$1(changedFiles, diffContent, userDirection, logContext);
1472
1596
  const messages = [
1473
1597
  { role: "system", content: systemPrompt },
@@ -1483,7 +1607,13 @@ async function runAgenticCommit(config) {
1483
1607
  debugResponseFile,
1484
1608
  storage,
1485
1609
  logger: logger2,
1486
- openaiReasoning
1610
+ openaiReasoning,
1611
+ tokenBudget: tokenBudget || {
1612
+ max: 15e4,
1613
+ reserveForResponse: 4e3,
1614
+ strategy: "fifo",
1615
+ onBudgetExceeded: "compress"
1616
+ }
1487
1617
  };
1488
1618
  const result = await runAgentic(agenticConfig);
1489
1619
  const parsed = parseAgenticResult$1(result.finalMessage);
@@ -1496,26 +1626,10 @@ async function runAgenticCommit(config) {
1496
1626
  toolMetrics: result.toolMetrics
1497
1627
  };
1498
1628
  }
1499
- function buildSystemPrompt$1() {
1629
+ function buildSystemPrompt$2(toolGuidance) {
1500
1630
  return `You are an expert software engineer tasked with generating meaningful commit messages.
1501
1631
 
1502
- You have access to tools to understand changes deeply. Use them strategically based on what you see:
1503
-
1504
- ## When to Use Each Tool
1505
-
1506
- **Understanding What Changed:**
1507
- - get_file_content: Use when you need full context. Good for: seeing entire class/function being modified, checking imports, understanding overall structure
1508
- - analyze_diff_section: Use when diff is confusing. Good for: expanding context around small changes, seeing how code integrates
1509
- - get_file_dependencies: Use for import/refactor changes. Good for: understanding what's being moved/reorganized, checking dependency impact
1510
-
1511
- **Understanding Why:**
1512
- - get_file_history: Use to see evolution. Good for: understanding if this continues previous work, checking for patterns
1513
- - get_recent_commits: Use to check recent context. Good for: avoiding duplicate messages, understanding if this is part of a series
1514
- - search_codebase: Use to understand usage. Good for: seeing if changes affect multiple places, finding patterns
1515
-
1516
- **Organizing Changes:**
1517
- - group_files_by_concern: Use when multiple files changed. Good for: identifying logical groupings, determining if split is needed
1518
- - get_related_tests: Use for logic changes. Good for: understanding intent from test changes, verifying behavior changes
1632
+ ${toolGuidance}
1519
1633
 
1520
1634
  ## Investigation Strategy
1521
1635
 
@@ -1976,6 +2090,9 @@ function createGetTagHistoryTool() {
1976
2090
  return {
1977
2091
  name: "get_tag_history",
1978
2092
  description: "Get the history of previous release tags to understand release patterns and versioning",
2093
+ category: "Release Analysis",
2094
+ cost: "cheap",
2095
+ examples: [{ scenario: "Check recent version tags", params: { limit: 5, pattern: "v*" }, expectedResult: "List of recent version tags with dates" }],
1979
2096
  parameters: {
1980
2097
  type: "object",
1981
2098
  properties: {
@@ -2025,6 +2142,9 @@ function createComparePreviousReleaseTool() {
2025
2142
  return {
2026
2143
  name: "compare_previous_release",
2027
2144
  description: "Compare this release with a previous release to understand what changed between versions",
2145
+ category: "Release Analysis",
2146
+ cost: "moderate",
2147
+ examples: [{ scenario: "Compare with last release", params: { previousTag: "v1.0.0", currentRef: "HEAD" }, expectedResult: "Commit count and file change statistics" }],
2028
2148
  parameters: {
2029
2149
  type: "object",
2030
2150
  properties: {
@@ -2080,6 +2200,9 @@ function createGetReleaseStatsTool() {
2080
2200
  return {
2081
2201
  name: "get_release_stats",
2082
2202
  description: "Get comprehensive statistics about the release including contributors, file changes, and commit patterns",
2203
+ category: "Release Analysis",
2204
+ cost: "moderate",
2205
+ examples: [{ scenario: "Get release overview", params: { fromRef: "v1.0.0", toRef: "HEAD" }, expectedResult: "Contributors, file changes, and top modified files" }],
2083
2206
  parameters: {
2084
2207
  type: "object",
2085
2208
  properties: {
@@ -2133,6 +2256,9 @@ function createGetBreakingChangesTool() {
2133
2256
  return {
2134
2257
  name: "get_breaking_changes",
2135
2258
  description: "Search for potential breaking changes by looking for specific patterns in commits and diffs",
2259
+ category: "Release Analysis",
2260
+ cost: "expensive",
2261
+ examples: [{ scenario: "Check for breaking changes", params: { fromRef: "v1.0.0", toRef: "HEAD" }, expectedResult: "List of potential breaking changes found" }],
2136
2262
  parameters: {
2137
2263
  type: "object",
2138
2264
  properties: {
@@ -2196,6 +2322,9 @@ function createAnalyzeCommitPatternsTool() {
2196
2322
  return {
2197
2323
  name: "analyze_commit_patterns",
2198
2324
  description: "Analyze commit messages to identify patterns and themes in the release",
2325
+ category: "Release Analysis",
2326
+ cost: "cheap",
2327
+ examples: [{ scenario: "Find commit themes", params: { fromRef: "v1.0.0", toRef: "HEAD" }, expectedResult: "Commit types and top keywords" }],
2199
2328
  parameters: {
2200
2329
  type: "object",
2201
2330
  properties: {
@@ -2267,7 +2396,8 @@ async function runAgenticRelease(config) {
2267
2396
  debugResponseFile,
2268
2397
  storage,
2269
2398
  logger: logger2,
2270
- openaiReasoning
2399
+ openaiReasoning,
2400
+ tokenBudget
2271
2401
  } = config;
2272
2402
  const toolRegistry = createToolRegistry({
2273
2403
  workingDirectory: process.cwd(),
@@ -2276,7 +2406,13 @@ async function runAgenticRelease(config) {
2276
2406
  });
2277
2407
  const tools = createReleaseTools();
2278
2408
  toolRegistry.registerAll(tools);
2279
- const systemPrompt = buildSystemPrompt();
2409
+ const toolGuidance = generateToolGuidance(tools, {
2410
+ strategy: "adaptive",
2411
+ includeExamples: true,
2412
+ explainWhenToUse: true,
2413
+ includeCategories: true
2414
+ });
2415
+ const systemPrompt = buildSystemPrompt$1(toolGuidance);
2280
2416
  const userMessage = buildUserMessage({
2281
2417
  fromRef,
2282
2418
  toRef,
@@ -2300,7 +2436,13 @@ async function runAgenticRelease(config) {
2300
2436
  debugResponseFile,
2301
2437
  storage,
2302
2438
  logger: logger2,
2303
- openaiReasoning
2439
+ openaiReasoning,
2440
+ tokenBudget: tokenBudget || {
2441
+ max: 2e5,
2442
+ reserveForResponse: 8e3,
2443
+ strategy: "fifo",
2444
+ onBudgetExceeded: "compress"
2445
+ }
2304
2446
  };
2305
2447
  const result = await runAgentic(agenticConfig);
2306
2448
  const parsed = parseAgenticResult(result.finalMessage);
@@ -2312,17 +2454,10 @@ async function runAgenticRelease(config) {
2312
2454
  toolMetrics: result.toolMetrics
2313
2455
  };
2314
2456
  }
2315
- function buildSystemPrompt() {
2457
+ function buildSystemPrompt$1(toolGuidance) {
2316
2458
  return `You are an expert software engineer and technical writer tasked with generating comprehensive, thoughtful release notes.
2317
2459
 
2318
- You have access to tools to investigate the release in depth. Use them strategically:
2319
-
2320
- ## Investigation Tools
2321
-
2322
- **Understanding Context & History:**
2323
- - get_file_history: Use when you need to understand how a file evolved. Good for: seeing if a refactor is part of a larger pattern, understanding why certain decisions were made
2324
- - get_recent_commits: Use to see what happened to files recently. Good for: detecting related work, understanding if this is part of an ongoing effort
2325
- - compare_previous_release: Use to contextualize scope. Good for: comparing size/impact of this release vs previous ones, identifying if this is major/minor
2460
+ ${toolGuidance}
2326
2461
  - get_tag_history: Use early to understand release cadence. Good for: establishing context about project versioning patterns
2327
2462
 
2328
2463
  **Analyzing Current Changes:**
@@ -2418,8 +2553,13 @@ function parseAgenticResult(finalMessage) {
2418
2553
  if (jsonMatch) {
2419
2554
  try {
2420
2555
  const jsonStr = jsonMatch[1].trim();
2421
- const parsed = JSON.parse(jsonStr);
2422
- if (parsed.title && parsed.body) {
2556
+ let parsed;
2557
+ try {
2558
+ parsed = JSON.parse(jsonStr);
2559
+ } catch {
2560
+ parsed = null;
2561
+ }
2562
+ if (parsed && parsed.title && parsed.body) {
2423
2563
  return {
2424
2564
  releaseNotes: {
2425
2565
  title: parsed.title,
@@ -2455,36 +2595,1084 @@ function parseAgenticResult(finalMessage) {
2455
2595
  }
2456
2596
  };
2457
2597
  }
2598
+ function createPublishTools() {
2599
+ return [
2600
+ createCheckGitStatusTool(),
2601
+ createCheckBranchSyncTool(),
2602
+ createAnalyzeDivergenceTool(),
2603
+ createGetCommitLogTool(),
2604
+ createGetBranchInfoTool(),
2605
+ createSyncBranchTool(),
2606
+ createGetDiffStatsTool(),
2607
+ createCheckConflictsTool(),
2608
+ createResetBranchTool()
2609
+ ];
2610
+ }
2611
+ function createCheckGitStatusTool() {
2612
+ return {
2613
+ name: "check_git_status",
2614
+ description: "Check the current git repository status, including uncommitted changes, current branch, and repository state",
2615
+ parameters: {
2616
+ type: "object",
2617
+ properties: {
2618
+ showUntracked: {
2619
+ type: "boolean",
2620
+ description: "Include untracked files in output (default: false)",
2621
+ default: false
2622
+ }
2623
+ },
2624
+ required: []
2625
+ },
2626
+ execute: async (params, context) => {
2627
+ const { showUntracked = false } = params;
2628
+ const workingDir = context?.workingDirectory || process.cwd();
2629
+ try {
2630
+ const branchResult = await run("git branch --show-current", { cwd: workingDir });
2631
+ const currentBranch = branchResult.stdout.trim();
2632
+ const statusCmd = showUntracked ? "git status --porcelain" : "git status --porcelain --untracked-files=no";
2633
+ const statusResult = await run(statusCmd, { cwd: workingDir });
2634
+ const hasChanges = statusResult.stdout.trim().length > 0;
2635
+ const headResult = await run("git rev-parse --short HEAD", { cwd: workingDir });
2636
+ const headSha = headResult.stdout.trim();
2637
+ return {
2638
+ currentBranch,
2639
+ headSha,
2640
+ hasUncommittedChanges: hasChanges,
2641
+ statusOutput: statusResult.stdout
2642
+ };
2643
+ } catch (error) {
2644
+ throw new Error(`Failed to check git status: ${error.message}`);
2645
+ }
2646
+ }
2647
+ };
2648
+ }
2649
+ function createCheckBranchSyncTool() {
2650
+ return {
2651
+ name: "check_branch_sync",
2652
+ description: "Check if a local branch is synchronized with its remote counterpart. Returns sync status, local/remote SHAs, and whether the branch exists locally.",
2653
+ parameters: {
2654
+ type: "object",
2655
+ properties: {
2656
+ branchName: {
2657
+ type: "string",
2658
+ description: "Name of the branch to check"
2659
+ }
2660
+ },
2661
+ required: ["branchName"]
2662
+ },
2663
+ execute: async (params, _context) => {
2664
+ const { branchName } = params;
2665
+ try {
2666
+ const exists = await localBranchExists(branchName);
2667
+ if (!exists) {
2668
+ return {
2669
+ exists: false,
2670
+ message: `Branch '${branchName}' does not exist locally`
2671
+ };
2672
+ }
2673
+ const syncStatus = await isBranchInSyncWithRemote(branchName);
2674
+ return {
2675
+ exists: true,
2676
+ branchName,
2677
+ inSync: syncStatus.inSync,
2678
+ localSha: syncStatus.localSha,
2679
+ remoteSha: syncStatus.remoteSha,
2680
+ error: syncStatus.error,
2681
+ isDiverged: syncStatus.localSha !== syncStatus.remoteSha
2682
+ };
2683
+ } catch (error) {
2684
+ throw new Error(`Failed to check branch sync: ${error.message}`);
2685
+ }
2686
+ }
2687
+ };
2688
+ }
2689
+ function createAnalyzeDivergenceTool() {
2690
+ return {
2691
+ name: "analyze_divergence",
2692
+ description: "Analyze how two branches have diverged by showing commits unique to each branch",
2693
+ parameters: {
2694
+ type: "object",
2695
+ properties: {
2696
+ sourceBranch: {
2697
+ type: "string",
2698
+ description: "The source/local branch name"
2699
+ },
2700
+ targetBranch: {
2701
+ type: "string",
2702
+ description: 'The target/remote branch to compare against (e.g., "origin/main")'
2703
+ },
2704
+ maxCommits: {
2705
+ type: "number",
2706
+ description: "Maximum number of commits to show for each branch (default: 10)",
2707
+ default: 10
2708
+ }
2709
+ },
2710
+ required: ["sourceBranch", "targetBranch"]
2711
+ },
2712
+ execute: async (params, context) => {
2713
+ const { sourceBranch, targetBranch, maxCommits = 10 } = params;
2714
+ const workingDir = context?.workingDirectory || process.cwd();
2715
+ try {
2716
+ const sourceOnlyResult = await run(
2717
+ `git log ${targetBranch}..${sourceBranch} --oneline -n ${maxCommits}`,
2718
+ { cwd: workingDir }
2719
+ );
2720
+ const targetOnlyResult = await run(
2721
+ `git log ${sourceBranch}..${targetBranch} --oneline -n ${maxCommits}`,
2722
+ { cwd: workingDir }
2723
+ );
2724
+ const mergeBaseResult = await run(
2725
+ `git merge-base ${sourceBranch} ${targetBranch}`,
2726
+ { cwd: workingDir }
2727
+ );
2728
+ return {
2729
+ sourceBranch,
2730
+ targetBranch,
2731
+ mergeBase: mergeBaseResult.stdout.trim(),
2732
+ commitsInSourceOnly: sourceOnlyResult.stdout.trim() || "None",
2733
+ commitsInTargetOnly: targetOnlyResult.stdout.trim() || "None",
2734
+ isDiverged: sourceOnlyResult.stdout.trim().length > 0 && targetOnlyResult.stdout.trim().length > 0,
2735
+ canFastForward: sourceOnlyResult.stdout.trim().length === 0
2736
+ // Target is ahead, can fast-forward
2737
+ };
2738
+ } catch (error) {
2739
+ throw new Error(`Failed to analyze divergence: ${error.message}`);
2740
+ }
2741
+ }
2742
+ };
2743
+ }
2744
+ function createGetCommitLogTool() {
2745
+ return {
2746
+ name: "get_commit_log",
2747
+ description: "Get git commit log for a branch or commit range with detailed information",
2748
+ parameters: {
2749
+ type: "object",
2750
+ properties: {
2751
+ ref: {
2752
+ type: "string",
2753
+ description: 'Git ref (branch name, commit range like "main..working", or single commit)'
2754
+ },
2755
+ maxCommits: {
2756
+ type: "number",
2757
+ description: "Maximum number of commits to return (default: 20)",
2758
+ default: 20
2759
+ },
2760
+ format: {
2761
+ type: "string",
2762
+ description: 'Format: "oneline" for brief, "full" for detailed with message body',
2763
+ enum: ["oneline", "full"],
2764
+ default: "oneline"
2765
+ }
2766
+ },
2767
+ required: ["ref"]
2768
+ },
2769
+ execute: async (params, context) => {
2770
+ const { ref, maxCommits = 20, format = "oneline" } = params;
2771
+ const workingDir = context?.workingDirectory || process.cwd();
2772
+ try {
2773
+ const formatStr = format === "full" ? "%H%n%an <%ae>%n%ad%n%s%n%n%b%n---" : "%h - %s (%an, %ar)";
2774
+ const result = await run(
2775
+ `git log "${ref}" --format="${formatStr}" -n ${maxCommits}`,
2776
+ { cwd: workingDir }
2777
+ );
2778
+ return result.stdout || "No commits found";
2779
+ } catch (error) {
2780
+ throw new Error(`Failed to get commit log: ${error.message}`);
2781
+ }
2782
+ }
2783
+ };
2784
+ }
2785
+ function createGetBranchInfoTool() {
2786
+ return {
2787
+ name: "get_branch_info",
2788
+ description: "Get detailed information about a branch including its tracking info, last commit, and whether it exists remotely",
2789
+ parameters: {
2790
+ type: "object",
2791
+ properties: {
2792
+ branchName: {
2793
+ type: "string",
2794
+ description: "Name of the branch to inspect"
2795
+ }
2796
+ },
2797
+ required: ["branchName"]
2798
+ },
2799
+ execute: async (params, context) => {
2800
+ const { branchName } = params;
2801
+ const workingDir = context?.workingDirectory || process.cwd();
2802
+ try {
2803
+ const localExists = await localBranchExists(branchName);
2804
+ if (!localExists) {
2805
+ try {
2806
+ const remoteCheckResult = await run(`git ls-remote --heads origin ${branchName}`, { cwd: workingDir });
2807
+ const remoteExists = remoteCheckResult.stdout.trim().length > 0;
2808
+ return {
2809
+ branchName,
2810
+ existsLocally: false,
2811
+ existsRemotely: remoteExists,
2812
+ message: remoteExists ? `Branch exists remotely but not locally` : `Branch does not exist locally or remotely`
2813
+ };
2814
+ } catch {
2815
+ return {
2816
+ branchName,
2817
+ existsLocally: false,
2818
+ existsRemotely: false
2819
+ };
2820
+ }
2821
+ }
2822
+ let trackingBranch = null;
2823
+ try {
2824
+ const trackingResult = await run(`git rev-parse --abbrev-ref ${branchName}@{upstream}`, { cwd: workingDir });
2825
+ trackingBranch = trackingResult.stdout.trim();
2826
+ } catch {
2827
+ }
2828
+ const lastCommitResult = await run(
2829
+ `git log ${branchName} -1 --format="%H|%h|%s|%an|%ar"`,
2830
+ { cwd: workingDir }
2831
+ );
2832
+ const [fullSha, shortSha, subject, author, relativeDate] = lastCommitResult.stdout.trim().split("|");
2833
+ const countResult = await run(`git rev-list --count ${branchName}`, { cwd: workingDir });
2834
+ const commitCount = parseInt(countResult.stdout.trim(), 10);
2835
+ if (isNaN(commitCount)) {
2836
+ throw new Error(`Invalid commit count returned from git: ${countResult.stdout}`);
2837
+ }
2838
+ return {
2839
+ branchName,
2840
+ existsLocally: true,
2841
+ trackingBranch,
2842
+ lastCommit: {
2843
+ fullSha,
2844
+ shortSha,
2845
+ subject,
2846
+ author,
2847
+ relativeDate
2848
+ },
2849
+ commitCount
2850
+ };
2851
+ } catch (error) {
2852
+ throw new Error(`Failed to get branch info: ${error.message}`);
2853
+ }
2854
+ }
2855
+ };
2856
+ }
2857
+ function createSyncBranchTool() {
2858
+ return {
2859
+ name: "sync_branch",
2860
+ description: "Attempt to synchronize a local branch with its remote counterpart. Returns success status and details about what happened.",
2861
+ parameters: {
2862
+ type: "object",
2863
+ properties: {
2864
+ branchName: {
2865
+ type: "string",
2866
+ description: "Name of the branch to sync"
2867
+ }
2868
+ },
2869
+ required: ["branchName"]
2870
+ },
2871
+ execute: async (params, _context) => {
2872
+ const { branchName } = params;
2873
+ try {
2874
+ const syncResult = await safeSyncBranchWithRemote(branchName);
2875
+ return {
2876
+ branchName,
2877
+ success: syncResult.success,
2878
+ conflictResolutionRequired: syncResult.conflictResolutionRequired,
2879
+ error: syncResult.error,
2880
+ message: syncResult.success ? `Successfully synchronized ${branchName} with remote` : syncResult.conflictResolutionRequired ? `Sync failed due to merge conflicts in ${branchName}` : `Sync failed: ${syncResult.error}`
2881
+ };
2882
+ } catch (error) {
2883
+ throw new Error(`Failed to sync branch: ${error.message}`);
2884
+ }
2885
+ }
2886
+ };
2887
+ }
2888
+ function createGetDiffStatsTool() {
2889
+ return {
2890
+ name: "get_diff_stats",
2891
+ description: "Get statistics about differences between two git refs (branches, commits, etc.)",
2892
+ parameters: {
2893
+ type: "object",
2894
+ properties: {
2895
+ fromRef: {
2896
+ type: "string",
2897
+ description: 'Starting ref (e.g., "main", "origin/main")'
2898
+ },
2899
+ toRef: {
2900
+ type: "string",
2901
+ description: 'Ending ref (e.g., "working", "HEAD")'
2902
+ },
2903
+ includeFileList: {
2904
+ type: "boolean",
2905
+ description: "Include list of changed files (default: true)",
2906
+ default: true
2907
+ }
2908
+ },
2909
+ required: ["fromRef", "toRef"]
2910
+ },
2911
+ execute: async (params, context) => {
2912
+ const { fromRef, toRef, includeFileList = true } = params;
2913
+ const workingDir = context?.workingDirectory || process.cwd();
2914
+ try {
2915
+ const statsResult = await run(`git diff --shortstat ${fromRef}..${toRef}`, { cwd: workingDir });
2916
+ let fileList = null;
2917
+ if (includeFileList) {
2918
+ const fileListResult = await run(`git diff --name-status ${fromRef}..${toRef}`, { cwd: workingDir });
2919
+ fileList = fileListResult.stdout;
2920
+ }
2921
+ return {
2922
+ fromRef,
2923
+ toRef,
2924
+ stats: statsResult.stdout.trim() || "No differences",
2925
+ fileList
2926
+ };
2927
+ } catch (error) {
2928
+ throw new Error(`Failed to get diff stats: ${error.message}`);
2929
+ }
2930
+ }
2931
+ };
2932
+ }
2933
+ function createCheckConflictsTool() {
2934
+ return {
2935
+ name: "check_conflicts",
2936
+ description: "Check if merging one branch into another would result in conflicts (without actually performing the merge)",
2937
+ parameters: {
2938
+ type: "object",
2939
+ properties: {
2940
+ sourceBranch: {
2941
+ type: "string",
2942
+ description: "Branch to merge from"
2943
+ },
2944
+ targetBranch: {
2945
+ type: "string",
2946
+ description: "Branch to merge into"
2947
+ }
2948
+ },
2949
+ required: ["sourceBranch", "targetBranch"]
2950
+ },
2951
+ execute: async (params, context) => {
2952
+ const { sourceBranch, targetBranch } = params;
2953
+ const workingDir = context?.workingDirectory || process.cwd();
2954
+ try {
2955
+ const mergeBaseResult = await run(
2956
+ `git merge-base ${targetBranch} ${sourceBranch}`,
2957
+ { cwd: workingDir }
2958
+ );
2959
+ const mergeBase = mergeBaseResult.stdout.trim();
2960
+ const mergeTreeResult = await run(
2961
+ `git merge-tree ${mergeBase} ${targetBranch} ${sourceBranch}`,
2962
+ { cwd: workingDir }
2963
+ );
2964
+ const hasConflicts = mergeTreeResult.stdout.includes("<<<<<<<") || mergeTreeResult.stdout.includes(">>>>>>>") || mergeTreeResult.stdout.includes("=======");
2965
+ const conflicts = hasConflicts ? mergeTreeResult.stdout.match(/\+\+\+ b\/([^\n]+)/g)?.map((m) => m.replace(/\+\+\+ b\//, "")) : [];
2966
+ return {
2967
+ sourceBranch,
2968
+ targetBranch,
2969
+ mergeBase,
2970
+ hasConflicts,
2971
+ conflictingFiles: conflicts || [],
2972
+ message: hasConflicts ? `Merging ${sourceBranch} into ${targetBranch} would create conflicts` : `Merging ${sourceBranch} into ${targetBranch} should succeed without conflicts`
2973
+ };
2974
+ } catch (error) {
2975
+ throw new Error(`Failed to check conflicts: ${error.message}`);
2976
+ }
2977
+ }
2978
+ };
2979
+ }
2980
+ function createResetBranchTool() {
2981
+ return {
2982
+ name: "reset_branch",
2983
+ description: "Reset a branch to match another ref (e.g., reset local main to origin/main). USE WITH CAUTION - this is a destructive operation.",
2984
+ parameters: {
2985
+ type: "object",
2986
+ properties: {
2987
+ branchName: {
2988
+ type: "string",
2989
+ description: "Name of the branch to reset"
2990
+ },
2991
+ targetRef: {
2992
+ type: "string",
2993
+ description: 'Ref to reset to (e.g., "origin/main")'
2994
+ },
2995
+ resetType: {
2996
+ type: "string",
2997
+ description: 'Type of reset: "hard" (discard changes), "soft" (keep changes staged), "mixed" (keep changes unstaged)',
2998
+ enum: ["hard", "soft", "mixed"],
2999
+ default: "hard"
3000
+ }
3001
+ },
3002
+ required: ["branchName", "targetRef"]
3003
+ },
3004
+ execute: async (params, context) => {
3005
+ const { branchName, targetRef, resetType = "hard" } = params;
3006
+ const workingDir = context?.workingDirectory || process.cwd();
3007
+ try {
3008
+ await run(`git checkout ${branchName}`, { cwd: workingDir });
3009
+ await run(`git reset --${resetType} ${targetRef}`, { cwd: workingDir });
3010
+ const headResult = await run("git rev-parse --short HEAD", { cwd: workingDir });
3011
+ const newHead = headResult.stdout.trim();
3012
+ return {
3013
+ branchName,
3014
+ targetRef,
3015
+ resetType,
3016
+ newHead,
3017
+ message: `Successfully reset ${branchName} to ${targetRef} (${resetType})`
3018
+ };
3019
+ } catch (error) {
3020
+ throw new Error(`Failed to reset branch: ${error.message}`);
3021
+ }
3022
+ }
3023
+ };
3024
+ }
3025
+ async function runAgenticPublish(config) {
3026
+ const {
3027
+ targetBranch,
3028
+ sourceBranch,
3029
+ issue,
3030
+ issueDetails,
3031
+ workingDirectory = process.cwd(),
3032
+ model = "gpt-4o",
3033
+ maxIterations = 10,
3034
+ debug = false,
3035
+ storage,
3036
+ logger: logger2,
3037
+ dryRun = false
3038
+ } = config;
3039
+ const toolContext = {
3040
+ workingDirectory,
3041
+ storage,
3042
+ logger: logger2
3043
+ };
3044
+ const tools = createToolRegistry(toolContext);
3045
+ tools.registerAll(createPublishTools());
3046
+ const systemPrompt = buildSystemPrompt(issue, dryRun);
3047
+ const userPrompt = buildUserPrompt({
3048
+ issue,
3049
+ targetBranch,
3050
+ sourceBranch,
3051
+ issueDetails
3052
+ });
3053
+ const messages = [
3054
+ {
3055
+ role: "system",
3056
+ content: systemPrompt
3057
+ },
3058
+ {
3059
+ role: "user",
3060
+ content: userPrompt
3061
+ }
3062
+ ];
3063
+ const executor = new AgenticExecutor(logger2);
3064
+ const result = await executor.run({
3065
+ messages,
3066
+ tools,
3067
+ model,
3068
+ maxIterations,
3069
+ debug,
3070
+ storage,
3071
+ logger: logger2
3072
+ });
3073
+ const analysis = parseAgentResponse(result.finalMessage);
3074
+ return {
3075
+ success: analysis.success,
3076
+ message: result.finalMessage,
3077
+ actionsTaken: analysis.actionsTaken,
3078
+ iterations: result.iterations,
3079
+ toolCallsExecuted: result.toolCallsExecuted,
3080
+ requiresManualIntervention: analysis.requiresManualIntervention,
3081
+ manualSteps: analysis.manualSteps
3082
+ };
3083
+ }
3084
+ function buildSystemPrompt(issue, dryRun) {
3085
+ const modeNote = dryRun ? "\n\nIMPORTANT: This is a DRY RUN. Do not use tools that make destructive changes (like reset_branch or sync_branch). Only use diagnostic tools to analyze the issue and provide recommendations." : "";
3086
+ return `You are an expert Git operations assistant helping to diagnose and fix issues that prevent publishing a software package.
3087
+
3088
+ Your role is to:
3089
+ 1. Use the available tools to investigate the problem
3090
+ 2. Analyze the root cause of the issue
3091
+ 3. ${dryRun ? "Recommend" : "Attempt to fix"} the issue using best practices
3092
+ 4. Provide clear explanations of what you found and ${dryRun ? "would do" : "did"}
3093
+
3094
+ Available tools allow you to:
3095
+ - Check git status and branch information
3096
+ - Analyze divergence between branches
3097
+ - View commit logs and diff statistics
3098
+ - Check for potential merge conflicts
3099
+ ${dryRun ? "" : "- Sync branches with remote\n- Reset branches to match remote refs"}
3100
+
3101
+ Guidelines:
3102
+ - Always diagnose before taking action
3103
+ - Prefer safe, non-destructive operations when possible
3104
+ - If a situation requires manual intervention, clearly state why and provide steps
3105
+ - Be thorough in your analysis
3106
+ - Consider the impact of each action on the repository state
3107
+
3108
+ Current issue type: ${issue}${modeNote}`;
3109
+ }
3110
+ function buildUserPrompt(params) {
3111
+ const { issue, targetBranch, sourceBranch, issueDetails } = params;
3112
+ const issueDescriptions = {
3113
+ branch_sync: `The target branch '${targetBranch}' is not synchronized with its remote counterpart. This prevents the publish workflow from proceeding safely.`,
3114
+ uncommitted_changes: `There are uncommitted changes in the working directory on branch '${sourceBranch}'. The publish workflow requires a clean working directory.`,
3115
+ merge_conflicts: `There are merge conflicts between '${sourceBranch}' and '${targetBranch}' that need to be resolved before publishing.`,
3116
+ unknown: `An unknown issue is preventing the publish workflow from proceeding.`
3117
+ };
3118
+ const basePrompt = `I'm trying to run a publish workflow that will:
3119
+ 1. Create a release from source branch '${sourceBranch}'
3120
+ 2. Merge it into target branch '${targetBranch}'
3121
+ 3. Publish the package
3122
+
3123
+ However, I'm encountering an issue:
3124
+
3125
+ ${issueDescriptions[issue] || issueDescriptions.unknown}`;
3126
+ const detailsSection = issueDetails ? `
3127
+
3128
+ Additional details:
3129
+ ${issueDetails}` : "";
3130
+ return `${basePrompt}${detailsSection}
3131
+
3132
+ Please investigate this issue and ${process.env.DRY_RUN ? "recommend how to fix it" : "attempt to fix it if possible"}. Use the available tools to:
3133
+
3134
+ 1. Diagnose the exact problem
3135
+ 2. Understand what caused it (e.g., what commits are causing divergence)
3136
+ 3. ${process.env.DRY_RUN ? "Recommend a solution" : "Attempt to resolve it safely"}
3137
+ 4. Explain what ${process.env.DRY_RUN ? "should be" : "was"} done and why
3138
+
3139
+ If the issue requires manual intervention, please explain why and provide clear steps.`;
3140
+ }
3141
+ function parseAgentResponse(message) {
3142
+ const successIndicators = [
3143
+ "successfully",
3144
+ "resolved",
3145
+ "fixed",
3146
+ "synced",
3147
+ "safe to proceed",
3148
+ "ready to publish"
3149
+ ];
3150
+ const manualInterventionIndicators = [
3151
+ "requires manual",
3152
+ "manual intervention",
3153
+ "cannot automatically",
3154
+ "you will need to",
3155
+ "please manually"
3156
+ ];
3157
+ const messageLower = message.toLowerCase();
3158
+ const hasSuccessIndicator = successIndicators.some((indicator) => messageLower.includes(indicator));
3159
+ const hasManualIndicator = manualInterventionIndicators.some((indicator) => messageLower.includes(indicator));
3160
+ const actionsTaken = [];
3161
+ const actionPatterns = [
3162
+ /(?:I |We )?(checked|analyzed|found|discovered|synced|reset|merged|fetched|pulled|compared|investigated)\s+([^.\n]+)/gi,
3163
+ /(?:Successfully|Successfully)\s+([^.\n]+)/gi
3164
+ ];
3165
+ for (const pattern of actionPatterns) {
3166
+ const matches = message.matchAll(pattern);
3167
+ for (const match of matches) {
3168
+ actionsTaken.push(match[0].trim());
3169
+ }
3170
+ }
3171
+ const manualSteps = [];
3172
+ const stepPatterns = [
3173
+ /(?:Step \d+:|^\d+\.)\s*(.+)$/gim,
3174
+ /^[-*]\s+(.+)$/gim
3175
+ ];
3176
+ if (hasManualIndicator) {
3177
+ for (const pattern of stepPatterns) {
3178
+ const matches = message.matchAll(pattern);
3179
+ for (const match of matches) {
3180
+ const step = match[1]?.trim();
3181
+ if (step && step.length > 10) {
3182
+ manualSteps.push(step);
3183
+ }
3184
+ }
3185
+ }
3186
+ }
3187
+ return {
3188
+ success: hasSuccessIndicator && !hasManualIndicator,
3189
+ actionsTaken: [...new Set(actionsTaken)],
3190
+ // Remove duplicates
3191
+ requiresManualIntervention: hasManualIndicator,
3192
+ manualSteps: [...new Set(manualSteps)]
3193
+ // Remove duplicates
3194
+ };
3195
+ }
3196
+ function formatAgenticPublishResult(result) {
3197
+ const lines = [];
3198
+ lines.push("");
3199
+ lines.push("═══════════════════════════════════════════════════════════");
3200
+ lines.push(" AGENTIC PUBLISH RECOVERY REPORT");
3201
+ lines.push("═══════════════════════════════════════════════════════════");
3202
+ lines.push("");
3203
+ if (result.success) {
3204
+ lines.push("✅ Status: RESOLVED");
3205
+ } else if (result.requiresManualIntervention) {
3206
+ lines.push("⚠️ Status: MANUAL INTERVENTION REQUIRED");
3207
+ } else {
3208
+ lines.push("❌ Status: UNRESOLVED");
3209
+ }
3210
+ lines.push("");
3211
+ lines.push(`Iterations: ${result.iterations}`);
3212
+ lines.push(`Tools executed: ${result.toolCallsExecuted}`);
3213
+ lines.push("");
3214
+ if (result.actionsTaken.length > 0) {
3215
+ lines.push("Actions taken:");
3216
+ for (const action of result.actionsTaken) {
3217
+ lines.push(` • ${action}`);
3218
+ }
3219
+ lines.push("");
3220
+ }
3221
+ if (result.requiresManualIntervention && result.manualSteps && result.manualSteps.length > 0) {
3222
+ lines.push("Manual steps required:");
3223
+ for (let i = 0; i < result.manualSteps.length; i++) {
3224
+ lines.push(` ${i + 1}. ${result.manualSteps[i]}`);
3225
+ }
3226
+ lines.push("");
3227
+ }
3228
+ lines.push("Detailed analysis:");
3229
+ lines.push("───────────────────────────────────────────────────────────");
3230
+ lines.push(result.message);
3231
+ lines.push("───────────────────────────────────────────────────────────");
3232
+ lines.push("");
3233
+ if (result.success) {
3234
+ lines.push("You can now retry the publish command.");
3235
+ } else if (result.requiresManualIntervention) {
3236
+ lines.push("Please complete the manual steps above, then retry the publish command.");
3237
+ } else {
3238
+ lines.push("The issue could not be resolved automatically. Please review the analysis above.");
3239
+ }
3240
+ lines.push("");
3241
+ return lines.join("\n");
3242
+ }
3243
+ function createConversationLogger(config) {
3244
+ const {
3245
+ outputDir,
3246
+ format = "json",
3247
+ storage,
3248
+ logger: logger2
3249
+ } = config;
3250
+ return {
3251
+ /**
3252
+ * Save a conversation to disk
3253
+ */
3254
+ async save(conversationId, messages, metadata) {
3255
+ try {
3256
+ if (!storage) {
3257
+ throw new Error("Storage adapter required");
3258
+ }
3259
+ const conversation = {
3260
+ id: conversationId,
3261
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3262
+ messages,
3263
+ metadata: metadata || {}
3264
+ };
3265
+ const filename = `${conversationId}.${format}`;
3266
+ const filepath = path__default.join(outputDir, filename);
3267
+ const content = format === "json" ? JSON.stringify(conversation, null, 2) : formatConversationAsMarkdown(conversation);
3268
+ await storage.writeOutput(filename, content);
3269
+ if (logger2) {
3270
+ logger2.debug(`Logged conversation ${conversationId} to ${filepath}`);
3271
+ }
3272
+ return filepath;
3273
+ } catch (error) {
3274
+ if (logger2) {
3275
+ logger2.warn(`Failed to log conversation ${conversationId}: ${error.message}`);
3276
+ }
3277
+ throw error;
3278
+ }
3279
+ },
3280
+ /**
3281
+ * Load a conversation from disk
3282
+ */
3283
+ async load(conversationId) {
3284
+ try {
3285
+ if (!storage) {
3286
+ throw new Error("Storage adapter required");
3287
+ }
3288
+ const filename = `${conversationId}.${format}`;
3289
+ const content = await storage.readFile(path__default.join(outputDir, filename), "utf-8");
3290
+ let conversation;
3291
+ if (format === "json") {
3292
+ try {
3293
+ conversation = JSON.parse(content);
3294
+ } catch (parseError) {
3295
+ throw new Error(`Failed to parse conversation JSON: ${parseError.message}`);
3296
+ }
3297
+ } else {
3298
+ conversation = content;
3299
+ }
3300
+ if (logger2) {
3301
+ logger2.info(`Loaded conversation ${conversationId}`);
3302
+ }
3303
+ return conversation;
3304
+ } catch (error) {
3305
+ if (logger2) {
3306
+ logger2.warn(`Failed to load conversation ${conversationId}: ${error.message}`);
3307
+ }
3308
+ throw error;
3309
+ }
3310
+ },
3311
+ /**
3312
+ * Get conversation summary
3313
+ */
3314
+ async getSummary(conversationId) {
3315
+ try {
3316
+ const conversation = await this.load(conversationId);
3317
+ return {
3318
+ id: conversation.id,
3319
+ timestamp: conversation.timestamp,
3320
+ messageCount: conversation.messages?.length || 0,
3321
+ metadata: conversation.metadata
3322
+ };
3323
+ } catch (error) {
3324
+ if (logger2) {
3325
+ logger2.warn(`Failed to get summary for ${conversationId}: ${error.message}`);
3326
+ }
3327
+ throw error;
3328
+ }
3329
+ },
3330
+ /**
3331
+ * Create riotprompt ConversationLogger instance for advanced usage
3332
+ */
3333
+ createRiotLogger() {
3334
+ const logConfig = {
3335
+ enabled: true,
3336
+ outputPath: outputDir,
3337
+ format,
3338
+ includeMetadata: true
3339
+ };
3340
+ return new ConversationLogger(logConfig, logger2);
3341
+ }
3342
+ };
3343
+ }
3344
+ function formatConversationAsMarkdown(conversation) {
3345
+ const lines = [];
3346
+ lines.push(`# Conversation: ${conversation.id}`);
3347
+ lines.push("");
3348
+ lines.push(`**Timestamp:** ${conversation.timestamp}`);
3349
+ if (conversation.metadata) {
3350
+ lines.push("");
3351
+ lines.push("## Metadata");
3352
+ lines.push("```json");
3353
+ lines.push(JSON.stringify(conversation.metadata, null, 2));
3354
+ lines.push("```");
3355
+ }
3356
+ lines.push("");
3357
+ lines.push("## Messages");
3358
+ lines.push("");
3359
+ for (let i = 0; i < conversation.messages.length; i++) {
3360
+ const msg = conversation.messages[i];
3361
+ lines.push(`### Message ${i + 1}: ${msg.role}`);
3362
+ lines.push("");
3363
+ lines.push(msg.content || "(no content)");
3364
+ lines.push("");
3365
+ }
3366
+ return lines.join("\n");
3367
+ }
3368
+ function generateConversationId(command) {
3369
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split(".")[0];
3370
+ return `${command}-${timestamp}`;
3371
+ }
3372
+ async function generateReflectionReport(options) {
3373
+ const {
3374
+ iterations,
3375
+ toolCallsExecuted,
3376
+ maxIterations,
3377
+ toolMetrics,
3378
+ conversationHistory,
3379
+ commitMessage,
3380
+ suggestedSplits,
3381
+ releaseNotes,
3382
+ logger: logger2
3383
+ } = options;
3384
+ const sections = [];
3385
+ sections.push("# Agentic Workflow - Self-Reflection Report");
3386
+ sections.push("");
3387
+ sections.push(`Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
3388
+ sections.push("");
3389
+ sections.push("## Execution Summary");
3390
+ sections.push("");
3391
+ sections.push(`- **Iterations**: ${iterations}${iterations >= maxIterations ? " (max reached)" : ""}`);
3392
+ sections.push(`- **Tool Calls**: ${toolCallsExecuted}`);
3393
+ sections.push(`- **Unique Tools**: ${new Set(toolMetrics.map((m) => m.name)).size}`);
3394
+ sections.push("");
3395
+ const toolStats = calculateToolStats(toolMetrics);
3396
+ sections.push("## Tool Effectiveness Analysis");
3397
+ sections.push("");
3398
+ if (toolStats.size === 0) {
3399
+ sections.push("*No tools were executed during this run.*");
3400
+ } else {
3401
+ sections.push("| Tool | Calls | Success | Failures | Success Rate | Avg Duration |");
3402
+ sections.push("|------|-------|---------|----------|--------------|--------------|");
3403
+ for (const [toolName, stats] of Array.from(toolStats.entries()).sort((a, b) => b[1].total - a[1].total)) {
3404
+ const successRate = stats.total > 0 ? (stats.success / stats.total * 100).toFixed(1) : "0.0";
3405
+ const avgDuration = stats.total > 0 ? (stats.totalDuration / stats.total).toFixed(0) : "0";
3406
+ sections.push(`| ${toolName} | ${stats.total} | ${stats.success} | ${stats.failures} | ${successRate}% | ${avgDuration}ms |`);
3407
+ }
3408
+ }
3409
+ sections.push("");
3410
+ if (toolStats.size > 0) {
3411
+ sections.push("### Tool Performance Insights");
3412
+ sections.push("");
3413
+ const failedTools = Array.from(toolStats.entries()).filter(([_, stats]) => stats.failures > 0);
3414
+ if (failedTools.length > 0) {
3415
+ sections.push("**Tools with Failures:**");
3416
+ for (const [toolName, stats] of failedTools) {
3417
+ const failureRate = stats.total > 0 ? (stats.failures / stats.total * 100).toFixed(1) : "0.0";
3418
+ sections.push(`- ${toolName}: ${stats.failures}/${stats.total} failures (${failureRate}%)`);
3419
+ }
3420
+ sections.push("");
3421
+ }
3422
+ const slowTools = Array.from(toolStats.entries()).filter(([_, stats]) => stats.total > 0 && stats.totalDuration / stats.total > 1e3).sort((a, b) => {
3423
+ const avgA = a[1].total > 0 ? a[1].totalDuration / a[1].total : 0;
3424
+ const avgB = b[1].total > 0 ? b[1].totalDuration / b[1].total : 0;
3425
+ return avgB - avgA;
3426
+ });
3427
+ if (slowTools.length > 0) {
3428
+ sections.push("**Slow Tools (>1s average):**");
3429
+ for (const [toolName, stats] of slowTools) {
3430
+ const avgDuration = stats.total > 0 ? (stats.totalDuration / stats.total / 1e3).toFixed(2) : "0.00";
3431
+ sections.push(`- ${toolName}: ${avgDuration}s average`);
3432
+ }
3433
+ sections.push("");
3434
+ }
3435
+ sections.push("**Most Frequently Used:**");
3436
+ const topTools = Array.from(toolStats.entries()).slice(0, 3);
3437
+ for (const [toolName, stats] of topTools) {
3438
+ sections.push(`- ${toolName}: ${stats.total} calls`);
3439
+ }
3440
+ sections.push("");
3441
+ }
3442
+ sections.push("## Recommendations for Improvement");
3443
+ sections.push("");
3444
+ const recommendations = generateRecommendations(options, toolStats);
3445
+ if (recommendations.length === 0) {
3446
+ sections.push("*No specific recommendations at this time. Execution appears optimal.*");
3447
+ } else {
3448
+ for (const rec of recommendations) {
3449
+ sections.push(rec);
3450
+ }
3451
+ }
3452
+ sections.push("");
3453
+ sections.push("## Detailed Execution Timeline");
3454
+ sections.push("");
3455
+ if (toolMetrics.length === 0) {
3456
+ sections.push("*No tool execution timeline available.*");
3457
+ } else {
3458
+ sections.push("| Time | Iteration | Tool | Result | Duration |");
3459
+ sections.push("|------|-----------|------|--------|----------|");
3460
+ for (const metric of toolMetrics) {
3461
+ const time = new Date(metric.timestamp).toLocaleTimeString();
3462
+ const result = metric.success ? "✅ Success" : `❌ ${metric.error || "Failed"}`;
3463
+ sections.push(`| ${time} | ${metric.iteration} | ${metric.name} | ${result} | ${metric.duration}ms |`);
3464
+ }
3465
+ sections.push("");
3466
+ }
3467
+ if (conversationHistory && conversationHistory.length > 0) {
3468
+ sections.push("## Conversation History");
3469
+ sections.push("");
3470
+ sections.push("<details>");
3471
+ sections.push("<summary>Click to expand full agentic interaction</summary>");
3472
+ sections.push("");
3473
+ sections.push("```json");
3474
+ sections.push(JSON.stringify(conversationHistory, null, 2));
3475
+ sections.push("```");
3476
+ sections.push("");
3477
+ sections.push("</details>");
3478
+ sections.push("");
3479
+ }
3480
+ if (commitMessage) {
3481
+ sections.push("## Generated Commit Message");
3482
+ sections.push("");
3483
+ sections.push("```");
3484
+ sections.push(commitMessage);
3485
+ sections.push("```");
3486
+ sections.push("");
3487
+ if (suggestedSplits && suggestedSplits.length > 1) {
3488
+ sections.push("## Suggested Commit Splits");
3489
+ sections.push("");
3490
+ for (let i = 0; i < suggestedSplits.length; i++) {
3491
+ const split = suggestedSplits[i];
3492
+ sections.push(`### Split ${i + 1}`);
3493
+ sections.push("");
3494
+ sections.push(`**Files**: ${split.files.join(", ")}`);
3495
+ sections.push("");
3496
+ sections.push(`**Rationale**: ${split.rationale}`);
3497
+ sections.push("");
3498
+ sections.push(`**Message**:`);
3499
+ sections.push("```");
3500
+ sections.push(split.message);
3501
+ sections.push("```");
3502
+ sections.push("");
3503
+ }
3504
+ }
3505
+ }
3506
+ if (releaseNotes) {
3507
+ sections.push("## Generated Release Notes");
3508
+ sections.push("");
3509
+ sections.push("### Title");
3510
+ sections.push("```");
3511
+ sections.push(releaseNotes.title);
3512
+ sections.push("```");
3513
+ sections.push("");
3514
+ sections.push("### Body");
3515
+ sections.push("```markdown");
3516
+ sections.push(releaseNotes.body);
3517
+ sections.push("```");
3518
+ sections.push("");
3519
+ }
3520
+ if (logger2) {
3521
+ logger2.debug(`Generated reflection report with ${toolStats.size} unique tools`);
3522
+ }
3523
+ return sections.join("\n");
3524
+ }
3525
+ function calculateToolStats(toolMetrics) {
3526
+ const stats = /* @__PURE__ */ new Map();
3527
+ for (const metric of toolMetrics) {
3528
+ if (!stats.has(metric.name)) {
3529
+ stats.set(metric.name, { total: 0, success: 0, failures: 0, totalDuration: 0 });
3530
+ }
3531
+ const toolStat = stats.get(metric.name);
3532
+ toolStat.total++;
3533
+ toolStat.totalDuration += metric.duration;
3534
+ if (metric.success) {
3535
+ toolStat.success++;
3536
+ } else {
3537
+ toolStat.failures++;
3538
+ }
3539
+ }
3540
+ return stats;
3541
+ }
3542
+ function generateRecommendations(options, toolStats) {
3543
+ const recommendations = [];
3544
+ const toolsWithFailures = Array.from(toolStats.entries()).filter(([_, stats]) => stats.failures > 0);
3545
+ if (toolsWithFailures.length > 0) {
3546
+ recommendations.push("- **Tool Failures**: Investigate and fix tools that are failing. This may indicate issues with error handling or tool implementation.");
3547
+ }
3548
+ const slowTools = Array.from(toolStats.entries()).filter(([_, stats]) => stats.total > 0 && stats.totalDuration / stats.total > 1e3);
3549
+ if (slowTools.length > 0) {
3550
+ recommendations.push("- **Performance**: Consider optimizing slow tools or caching results to improve execution speed.");
3551
+ }
3552
+ if (options.iterations >= options.maxIterations) {
3553
+ recommendations.push("- **Max Iterations Reached**: The agent reached maximum iterations. Consider increasing the limit or improving tool efficiency to allow the agent to complete naturally.");
3554
+ }
3555
+ const underutilizedTools = Array.from(toolStats.entries()).filter(([_, stats]) => stats.total === 1);
3556
+ if (underutilizedTools.length > 3) {
3557
+ recommendations.push("- **Underutilized Tools**: Many tools were called only once. Consider whether all tools are necessary or if the agent needs better guidance on when to use them.");
3558
+ }
3559
+ const uniqueTools = toolStats.size;
3560
+ if (uniqueTools === 1 && options.toolCallsExecuted > 3) {
3561
+ recommendations.push("- **Low Tool Diversity**: Only one tool was used despite multiple calls. Consider if the agent needs better guidance to use a variety of tools.");
3562
+ }
3563
+ return recommendations;
3564
+ }
3565
+ async function saveReflectionReport(report, outputPath, storage) {
3566
+ if (!storage) {
3567
+ throw new Error("Storage adapter required to save report");
3568
+ }
3569
+ const filename = outputPath.split("/").pop() || "reflection.md";
3570
+ await storage.writeOutput(filename, report);
3571
+ }
3572
+ function createMetricsCollector(config = {}) {
3573
+ const { logger: logger2 } = config;
3574
+ const collector = new MetricsCollector(logger2);
3575
+ const toolMetrics = [];
3576
+ return {
3577
+ /**
3578
+ * Record a tool execution
3579
+ */
3580
+ recordToolExecution(metric) {
3581
+ toolMetrics.push(metric);
3582
+ collector.recordToolCall(
3583
+ metric.name,
3584
+ metric.iteration,
3585
+ metric.duration,
3586
+ metric.success,
3587
+ metric.error
3588
+ );
3589
+ if (logger2) {
3590
+ const status = metric.success ? "success" : "failed";
3591
+ logger2.debug(`Tool ${metric.name} ${status} in ${metric.duration}ms`);
3592
+ }
3593
+ },
3594
+ /**
3595
+ * Record an iteration
3596
+ */
3597
+ recordIteration() {
3598
+ collector.incrementIteration();
3599
+ if (logger2) {
3600
+ logger2.debug(`Iteration incremented`);
3601
+ }
3602
+ },
3603
+ /**
3604
+ * Get tool metrics in legacy format (for backward compatibility)
3605
+ */
3606
+ getToolMetrics() {
3607
+ return [...toolMetrics];
3608
+ },
3609
+ /**
3610
+ * Generate execution metrics report
3611
+ */
3612
+ generateReport() {
3613
+ return collector.getMetrics([]);
3614
+ },
3615
+ /**
3616
+ * Get summary statistics
3617
+ */
3618
+ getSummary() {
3619
+ const report = collector.getMetrics([]);
3620
+ return {
3621
+ totalIterations: report.iterations,
3622
+ totalToolCalls: report.toolCallsExecuted,
3623
+ totalDuration: report.totalDuration,
3624
+ toolMetrics: report.toolMetrics,
3625
+ toolStats: report.toolStats
3626
+ };
3627
+ },
3628
+ /**
3629
+ * Reset all metrics
3630
+ */
3631
+ reset() {
3632
+ toolMetrics.length = 0;
3633
+ }
3634
+ };
3635
+ }
2458
3636
  export {
2459
3637
  AgenticExecutor,
2460
3638
  OpenAIError,
2461
3639
  STANDARD_CHOICES,
2462
3640
  SecureTempFile,
3641
+ TemplateNames,
2463
3642
  ToolRegistry,
2464
3643
  cleanupTempFile,
2465
3644
  createCommitPrompt,
2466
3645
  createCommitTools,
2467
3646
  createCompletion,
2468
3647
  createCompletionWithRetry,
3648
+ createConversationLogger,
3649
+ createMetricsCollector,
2469
3650
  createNoOpLogger,
3651
+ createPublishTools,
2470
3652
  createReleasePrompt,
2471
3653
  createReleaseTools,
2472
3654
  createReviewPrompt,
2473
3655
  createSecureTempFile,
2474
3656
  createToolRegistry,
2475
3657
  editContentInEditor,
3658
+ formatAgenticPublishResult,
3659
+ generateConversationId,
3660
+ generateReflectionReport,
2476
3661
  getLLMFeedbackInEditor,
2477
3662
  getLogger,
2478
3663
  getModelForCommand,
2479
3664
  getOpenAIReasoningForCommand,
2480
3665
  getUserChoice,
2481
3666
  getUserTextInput,
3667
+ initializeTemplates,
2482
3668
  isRateLimitError,
2483
3669
  isTokenLimitError,
2484
3670
  requireTTY,
2485
3671
  runAgentic,
2486
3672
  runAgenticCommit,
3673
+ runAgenticPublish,
2487
3674
  runAgenticRelease,
3675
+ saveReflectionReport,
2488
3676
  setLogger,
2489
3677
  transcribeAudio,
2490
3678
  tryLoadWinston