@link-assistant/hive-mind 1.2.2 → 1.2.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 5411e77: Fix gh-upload-log command invocation error caused by empty string argument
8
+ - Fixed bug where `gh-upload-log` failed with "Unknown argument: ''" when verbose=false
9
+ - The issue was caused by template literal interpolation `${verbose ? '--verbose' : ''}` passing empty string as an argument
10
+ - Now using array-based command building to avoid empty arguments
11
+ - Added improved handling for `error_during_execution` result subtype from Claude CLI
12
+ - Added tests for log upload command construction to prevent regression
13
+
3
14
  ## 1.2.2
4
15
 
5
16
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -680,6 +680,24 @@ const displayModelUsage = async (usage, log) => {
680
680
  await log(' Cost: Not available (could not fetch pricing)');
681
681
  }
682
682
  };
683
+ /**
684
+ * Display cost comparison between public pricing and Anthropic's official cost
685
+ * @param {number|null} publicCost - Public pricing estimate
686
+ * @param {number|null} anthropicCost - Anthropic's official cost
687
+ * @param {Function} log - Logging function
688
+ */
689
+ const displayCostComparison = async (publicCost, anthropicCost, log) => {
690
+ await log('\n šŸ’° Cost estimation:');
691
+ await log(` Public pricing estimate: ${publicCost !== null && publicCost !== undefined ? `$${publicCost.toFixed(6)} USD` : 'unknown'}`);
692
+ await log(` Calculated by Anthropic: ${anthropicCost !== null && anthropicCost !== undefined ? `$${anthropicCost.toFixed(6)} USD` : 'unknown'}`);
693
+ if (publicCost !== null && publicCost !== undefined && anthropicCost !== null && anthropicCost !== undefined) {
694
+ const difference = anthropicCost - publicCost;
695
+ const percentDiff = publicCost > 0 ? (difference / publicCost) * 100 : 0;
696
+ await log(` Difference: $${difference.toFixed(6)} (${percentDiff > 0 ? '+' : ''}${percentDiff.toFixed(2)}%)`);
697
+ } else {
698
+ await log(' Difference: unknown');
699
+ }
700
+ };
683
701
  export const calculateSessionTokens = async (sessionId, tempDir) => {
684
702
  const os = (await use('os')).default;
685
703
  const homeDir = os.homedir();
@@ -885,6 +903,7 @@ export const executeClaudeCommand = async params => {
885
903
  let is503Error = false;
886
904
  let stderrErrors = [];
887
905
  let anthropicTotalCostUSD = null; // Capture Anthropic's official total_cost_usd from result
906
+ let errorDuringExecution = false; // Issue #1088: Track if error_during_execution subtype occurred
888
907
 
889
908
  // Create interactive mode handler if enabled
890
909
  let interactiveHandler = null;
@@ -1011,21 +1030,24 @@ export const executeClaudeCommand = async params => {
1011
1030
  } else if (data.type === 'tool_use') {
1012
1031
  toolUseCount++;
1013
1032
  }
1014
- // Handle session result type from Claude CLI
1015
- // This is emitted when a session completes, either successfully or with an error
1016
- // Example: {"type": "result", "subtype": "success", "is_error": true, "result": "Session limit reached āˆ™ resets 10am"}
1033
+ // Handle session result type from Claude CLI (emitted when session completes)
1034
+ // Subtypes: "success", "error_during_execution" (work may have been done), etc.
1017
1035
  if (data.type === 'result') {
1018
- // Capture Anthropic's official total_cost_usd from the result
1019
1036
  if (data.total_cost_usd !== undefined && data.total_cost_usd !== null) {
1020
1037
  anthropicTotalCostUSD = data.total_cost_usd;
1021
- await log(`šŸ’° Anthropic official cost captured: $${anthropicTotalCostUSD.toFixed(6)}`, {
1022
- verbose: true,
1023
- });
1038
+ await log(`šŸ’° Anthropic official cost captured: $${anthropicTotalCostUSD.toFixed(6)}`, { verbose: true });
1024
1039
  }
1025
1040
  if (data.is_error === true) {
1026
- commandFailed = true;
1027
1041
  lastMessage = data.result || JSON.stringify(data);
1028
- await log('āš ļø Detected error result from Claude CLI', { verbose: true });
1042
+ const subtype = data.subtype || 'unknown';
1043
+ // Issue #1088: "error_during_execution" = warning (work may exist), others = failure
1044
+ if (subtype === 'error_during_execution') {
1045
+ errorDuringExecution = true;
1046
+ await log(`āš ļø Error during execution (subtype: ${subtype}) - work may be completed`, { verbose: true });
1047
+ } else {
1048
+ commandFailed = true;
1049
+ await log(`āš ļø Detected error from Claude CLI (subtype: ${subtype})`, { verbose: true });
1050
+ }
1029
1051
  if (lastMessage.includes('Session limit reached') || lastMessage.includes('limit reached')) {
1030
1052
  limitReached = true;
1031
1053
  await log('āš ļø Detected session limit in result', { verbose: true });
@@ -1264,9 +1286,16 @@ export const executeClaudeCommand = async params => {
1264
1286
  limitResetTime,
1265
1287
  messageCount,
1266
1288
  toolUseCount,
1289
+ errorDuringExecution,
1267
1290
  };
1268
1291
  }
1269
- await log('\n\nāœ… Claude command completed');
1292
+ // Issue #1088: If error_during_execution occurred but command didn't fail,
1293
+ // log it as "Finished with errors" instead of pure success
1294
+ if (errorDuringExecution) {
1295
+ await log('\n\nāš ļø Claude command finished with errors');
1296
+ } else {
1297
+ await log('\n\nāœ… Claude command completed');
1298
+ }
1270
1299
  await log(`šŸ“Š Total messages: ${messageCount}, Tool uses: ${toolUseCount}`);
1271
1300
  // Calculate and display total token usage from session JSONL file
1272
1301
  if (sessionId && tempDir) {
@@ -1289,49 +1318,11 @@ export const executeClaudeCommand = async params => {
1289
1318
  // Show totals if multiple models were used
1290
1319
  if (modelIds.length > 1) {
1291
1320
  await log('\n šŸ“ˆ Total across all models:');
1292
- // Show cost comparison
1293
- await log('\n šŸ’° Cost estimation:');
1294
- if (tokenUsage.totalCostUSD !== null && tokenUsage.totalCostUSD !== undefined) {
1295
- await log(` Public pricing estimate: $${tokenUsage.totalCostUSD.toFixed(6)} USD`);
1296
- } else {
1297
- await log(' Public pricing estimate: unknown');
1298
- }
1299
- if (anthropicTotalCostUSD !== null && anthropicTotalCostUSD !== undefined) {
1300
- await log(` Calculated by Anthropic: $${anthropicTotalCostUSD.toFixed(6)} USD`);
1301
- // Show comparison if both are available
1302
- if (tokenUsage.totalCostUSD !== null && tokenUsage.totalCostUSD !== undefined) {
1303
- const difference = anthropicTotalCostUSD - tokenUsage.totalCostUSD;
1304
- const percentDiff = tokenUsage.totalCostUSD > 0 ? (difference / tokenUsage.totalCostUSD) * 100 : 0;
1305
- await log(` Difference: $${difference.toFixed(6)} (${percentDiff > 0 ? '+' : ''}${percentDiff.toFixed(2)}%)`);
1306
- } else {
1307
- await log(' Difference: unknown');
1308
- }
1309
- } else {
1310
- await log(' Calculated by Anthropic: unknown');
1311
- await log(' Difference: unknown');
1312
- }
1313
- } else {
1314
- // Single model - show cost comparison
1315
- await log('\n šŸ’° Cost estimation:');
1316
- if (tokenUsage.totalCostUSD !== null && tokenUsage.totalCostUSD !== undefined) {
1317
- await log(` Public pricing estimate: $${tokenUsage.totalCostUSD.toFixed(6)} USD`);
1318
- } else {
1319
- await log(' Public pricing estimate: unknown');
1320
- }
1321
- if (anthropicTotalCostUSD !== null && anthropicTotalCostUSD !== undefined) {
1322
- await log(` Calculated by Anthropic: $${anthropicTotalCostUSD.toFixed(6)} USD`);
1323
- // Show comparison if both are available
1324
- if (tokenUsage.totalCostUSD !== null && tokenUsage.totalCostUSD !== undefined) {
1325
- const difference = anthropicTotalCostUSD - tokenUsage.totalCostUSD;
1326
- const percentDiff = tokenUsage.totalCostUSD > 0 ? (difference / tokenUsage.totalCostUSD) * 100 : 0;
1327
- await log(` Difference: $${difference.toFixed(6)} (${percentDiff > 0 ? '+' : ''}${percentDiff.toFixed(2)}%)`);
1328
- } else {
1329
- await log(' Difference: unknown');
1330
- }
1331
- } else {
1332
- await log(' Calculated by Anthropic: unknown');
1333
- await log(' Difference: unknown');
1334
- }
1321
+ }
1322
+ // Show cost comparison (for both single and multiple models)
1323
+ await displayCostComparison(tokenUsage.totalCostUSD, anthropicTotalCostUSD, log);
1324
+ // Show total tokens for single model only
1325
+ if (modelIds.length === 1) {
1335
1326
  await log(` Total tokens: ${formatNumber(tokenUsage.totalTokens)}`);
1336
1327
  }
1337
1328
  } else {
@@ -1365,6 +1356,7 @@ export const executeClaudeCommand = async params => {
1365
1356
  messageCount,
1366
1357
  toolUseCount,
1367
1358
  anthropicTotalCostUSD, // Pass Anthropic's official total cost
1359
+ errorDuringExecution, // Issue #1088: Track if error_during_execution subtype occurred
1368
1360
  };
1369
1361
  } catch (error) {
1370
1362
  reportError(error, {
@@ -380,6 +380,8 @@ export async function attachLogToGitHub(options) {
380
380
  // New parameters for agent tool pricing support
381
381
  publicPricingEstimate = null,
382
382
  pricingInfo = null,
383
+ // Issue #1088: Track error_during_execution for "Finished with errors" state
384
+ errorDuringExecution = false,
383
385
  } = options;
384
386
  const targetName = targetType === 'pr' ? 'Pull Request' : 'Issue';
385
387
  const ghCommand = targetType === 'pr' ? 'pr' : 'issue';
@@ -498,6 +500,25 @@ ${logContent}
498
500
 
499
501
  </details>
500
502
 
503
+ ---
504
+ *Now working session is ended, feel free to review and add any feedback on the solution draft.*`;
505
+ } else if (errorDuringExecution) {
506
+ // Issue #1088: "Finished with errors" format - work may have been completed but errors occurred
507
+ const costInfo = buildCostInfoString(totalCostUSD, anthropicTotalCostUSD, pricingInfo);
508
+ logComment = `## āš ļø Solution Draft Finished with Errors
509
+ This log file contains the complete execution trace of the AI ${targetType === 'pr' ? 'solution draft' : 'analysis'} process.${costInfo}
510
+
511
+ **Note**: The session encountered errors during execution, but some work may have been completed. Please review the changes carefully.
512
+
513
+ <details>
514
+ <summary>Click to expand solution draft log (${Math.round(logStats.size / 1024)}KB)</summary>
515
+
516
+ \`\`\`
517
+ ${logContent}
518
+ \`\`\`
519
+
520
+ </details>
521
+
501
522
  ---
502
523
  *Now working session is ended, feel free to review and add any feedback on the solution draft.*`;
503
524
  } else {
@@ -624,6 +645,18 @@ ${errorMessage}
624
645
  šŸ“Ž **Failure log uploaded as ${uploadTypeLabel}${chunkInfo}** (${Math.round(logStats.size / 1024)}KB)
625
646
  šŸ”— [View complete failure log](${logUrl})
626
647
  ---
648
+ *Now working session is ended, feel free to review and add any feedback on the solution draft.*`;
649
+ } else if (errorDuringExecution) {
650
+ // Issue #1088: "Finished with errors" format - work may have been completed but errors occurred
651
+ const costInfo = buildCostInfoString(totalCostUSD, anthropicTotalCostUSD, pricingInfo);
652
+ logUploadComment = `## āš ļø Solution Draft Finished with Errors
653
+ This log file contains the complete execution trace of the AI ${targetType === 'pr' ? 'solution draft' : 'analysis'} process.${costInfo}
654
+
655
+ **Note**: The session encountered errors during execution, but some work may have been completed. Please review the changes carefully.
656
+
657
+ šŸ“Ž **Log file uploaded as ${uploadTypeLabel}${chunkInfo}** (${Math.round(logStats.size / 1024)}KB)
658
+ šŸ”— [View complete solution draft log](${logUrl})
659
+ ---
627
660
  *Now working session is ended, feel free to review and add any feedback on the solution draft.*`;
628
661
  } else {
629
662
  // Success log format - use helper function for cost info
@@ -44,7 +44,12 @@ export const uploadLogWithGhUploadLog = async ({ logFile, isPublic, description,
44
44
  await log(` šŸ“¤ Running: ${command}`, { verbose: true });
45
45
  }
46
46
 
47
- const uploadResult = await $`gh-upload-log "${logFile}" ${publicFlag} ${verbose ? '--verbose' : ''}`;
47
+ // Build command arguments array, filtering out empty strings to prevent "Unknown argument: ''" error
48
+ const commandArgs = [`"${logFile}"`, publicFlag];
49
+ if (verbose) {
50
+ commandArgs.push('--verbose');
51
+ }
52
+ const uploadResult = await $`gh-upload-log ${commandArgs.join(' ')}`;
48
53
  const output = (uploadResult.stdout?.toString() || '') + (uploadResult.stderr?.toString() || '');
49
54
 
50
55
  if (uploadResult.code !== 0) {
package/src/solve.mjs CHANGED
@@ -891,6 +891,7 @@ try {
891
891
  let anthropicTotalCostUSD = toolResult.anthropicTotalCostUSD;
892
892
  let publicPricingEstimate = toolResult.publicPricingEstimate; // Used by agent tool
893
893
  let pricingInfo = toolResult.pricingInfo; // Used by agent tool for detailed pricing
894
+ let errorDuringExecution = toolResult.errorDuringExecution || false; // Issue #1088: Track error_during_execution
894
895
  limitReached = toolResult.limitReached;
895
896
  cleanupContext.limitReached = limitReached;
896
897
 
@@ -1127,7 +1128,8 @@ try {
1127
1128
  // Search for newly created pull requests and comments
1128
1129
  // Pass shouldRestart to prevent early exit when auto-restart is needed
1129
1130
  // Include agent tool pricing data when available (publicPricingEstimate, pricingInfo)
1130
- await verifyResults(owner, repo, branchName, issueNumber, prNumber, prUrl, referenceTime, argv, shouldAttachLogs, shouldRestart, sessionId, tempDir, anthropicTotalCostUSD, publicPricingEstimate, pricingInfo);
1131
+ // Issue #1088: Pass errorDuringExecution for "Finished with errors" state
1132
+ await verifyResults(owner, repo, branchName, issueNumber, prNumber, prUrl, referenceTime, argv, shouldAttachLogs, shouldRestart, sessionId, tempDir, anthropicTotalCostUSD, publicPricingEstimate, pricingInfo, errorDuringExecution);
1131
1133
 
1132
1134
  // Start watch mode if enabled OR if we need to handle uncommitted changes
1133
1135
  if (argv.verbose) {
@@ -414,7 +414,7 @@ export const showSessionSummary = async (sessionId, limitReached, argv, issueUrl
414
414
  };
415
415
 
416
416
  // Verify results by searching for new PRs and comments
417
- export const verifyResults = async (owner, repo, branchName, issueNumber, prNumber, prUrl, referenceTime, argv, shouldAttachLogs, shouldRestart = false, sessionId = null, tempDir = null, anthropicTotalCostUSD = null, publicPricingEstimate = null, pricingInfo = null) => {
417
+ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumber, prUrl, referenceTime, argv, shouldAttachLogs, shouldRestart = false, sessionId = null, tempDir = null, anthropicTotalCostUSD = null, publicPricingEstimate = null, pricingInfo = null, errorDuringExecution = false) => {
418
418
  await log('\nšŸ” Searching for created pull requests or comments...');
419
419
 
420
420
  try {
@@ -545,6 +545,8 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
545
545
  // Pass agent tool pricing data when available
546
546
  publicPricingEstimate,
547
547
  pricingInfo,
548
+ // Issue #1088: Pass errorDuringExecution for "Finished with errors" state
549
+ errorDuringExecution,
548
550
  });
549
551
  }
550
552
 
@@ -608,6 +610,8 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
608
610
  // Pass agent tool pricing data when available
609
611
  publicPricingEstimate,
610
612
  pricingInfo,
613
+ // Issue #1088: Pass errorDuringExecution for "Finished with errors" state
614
+ errorDuringExecution,
611
615
  });
612
616
  }
613
617