mrvn-cli 0.5.21 → 0.5.23

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.
@@ -19842,6 +19842,7 @@ function generateProposedActions(issues) {
19842
19842
  // src/skills/builtin/jira/sprint-progress.ts
19843
19843
  import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
19844
19844
  var DONE_STATUSES6 = /* @__PURE__ */ new Set(["done", "closed", "resolved", "obsolete", "wont do", "cancelled"]);
19845
+ var PROGRESS_DONE_STATUSES = /* @__PURE__ */ new Set(["done", "closed", "resolved", "obsolete", "wont do"]);
19845
19846
  var BATCH_SIZE = 5;
19846
19847
  var MAX_LINKED_ISSUES = 50;
19847
19848
  var BLOCKED_WEIGHT_RISK_THRESHOLD = 0.3;
@@ -20731,11 +20732,12 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20731
20732
  }
20732
20733
  }
20733
20734
  const primaryHasComments = jiraKey ? (jiraIssues.get(jiraKey)?.comments.length ?? 0) > 0 : false;
20735
+ let commentAnalysisProgress = null;
20734
20736
  if (depth < MAX_LLM_DEPTH && jiraKey && primaryHasComments) {
20735
20737
  const estimatedChars = estimateCommentTextSize(jiraIssues, linkedJiraIssues, linkedIssueSignals);
20736
20738
  if (estimatedChars <= MAX_LLM_COMMENT_CHARS) {
20737
20739
  try {
20738
- const summary = await analyzeSingleArtifactComments(
20740
+ const analysis = await analyzeSingleArtifactComments(
20739
20741
  fm.id,
20740
20742
  fm.title,
20741
20743
  jiraKey,
@@ -20744,7 +20746,20 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20744
20746
  linkedJiraIssues,
20745
20747
  linkedIssueSignals
20746
20748
  );
20747
- commentSummary = summary;
20749
+ commentSummary = analysis.summary;
20750
+ commentAnalysisProgress = analysis.progressEstimate;
20751
+ if (commentAnalysisProgress !== null) {
20752
+ const hasExplicitProgress = "progress" in fm && typeof fm.progress === "number";
20753
+ if (!hasExplicitProgress && !fm.progressOverride && commentAnalysisProgress !== currentProgress) {
20754
+ proposedUpdates.push({
20755
+ artifactId: fm.id,
20756
+ field: "progress",
20757
+ currentValue: currentProgress,
20758
+ proposedValue: commentAnalysisProgress,
20759
+ reason: `Comment analysis estimates ${commentAnalysisProgress}% progress`
20760
+ });
20761
+ }
20762
+ }
20748
20763
  } catch (err) {
20749
20764
  errors.push(`Comment analysis failed: ${err instanceof Error ? err.message : String(err)}`);
20750
20765
  }
@@ -20768,8 +20783,16 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20768
20783
  children.push(childReport);
20769
20784
  }
20770
20785
  if (children.length > 0) {
20786
+ const childProgressValues = children.map((c) => {
20787
+ const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
20788
+ const lastStatus = findLast(updates, (u) => u.field === "status");
20789
+ if (lastStatus && PROGRESS_DONE_STATUSES.has(String(lastStatus.proposedValue))) return 100;
20790
+ const lastProgress = findLast(updates, (u) => u.field === "progress");
20791
+ if (lastProgress) return lastProgress.proposedValue;
20792
+ return c.marvinProgress;
20793
+ });
20771
20794
  const rolledUpProgress = Math.round(
20772
- children.reduce((s, c) => s + c.marvinProgress, 0) / children.length
20795
+ childProgressValues.reduce((s, p) => s + p, 0) / childProgressValues.length
20773
20796
  );
20774
20797
  if (rolledUpProgress !== currentProgress) {
20775
20798
  proposedUpdates.push({
@@ -20785,7 +20808,7 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20785
20808
  const appliedUpdates = [];
20786
20809
  if (options.applyUpdates && proposedUpdates.length > 0) {
20787
20810
  const doneArtifacts = new Set(
20788
- proposedUpdates.filter((u) => u.field === "status" && DONE_STATUSES6.has(String(u.proposedValue))).map((u) => u.artifactId)
20811
+ proposedUpdates.filter((u) => u.field === "status" && PROGRESS_DONE_STATUSES.has(String(u.proposedValue))).map((u) => u.artifactId)
20789
20812
  );
20790
20813
  for (const update of proposedUpdates) {
20791
20814
  if (update.field === "review") continue;
@@ -20834,6 +20857,7 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20834
20857
  progressDrift,
20835
20858
  commentSignals,
20836
20859
  commentSummary,
20860
+ commentAnalysisProgress,
20837
20861
  linkedIssues,
20838
20862
  linkedIssueSignals,
20839
20863
  children,
@@ -20926,13 +20950,15 @@ function estimateCommentTextSize(jiraIssues, linkedJiraIssues, linkedIssueSignal
20926
20950
  }
20927
20951
  var SINGLE_ARTIFACT_COMMENT_PROMPT = `You are a delivery management assistant analyzing Jira comments for a single work item.
20928
20952
 
20929
- Produce a 2-3 sentence progress summary covering:
20930
- - What work has been completed
20931
- - What is pending or blocked
20932
- - Any decisions, handoffs, or scheduling mentioned
20933
- - Relevant context from linked issue comments (if provided)
20953
+ Analyze the comments and produce:
20954
+ 1. A 2-3 sentence progress summary covering: what work has been completed, what is pending or blocked, any decisions/handoffs/scheduling mentioned, and relevant context from linked issue comments (if provided).
20955
+ 2. A progress estimate (0-100%) based on evidence in the comments \u2014 e.g., if comments indicate all items have been triaged into tasks, or implementation is complete pending review, estimate accordingly. If you cannot determine progress from the comments, set progressEstimate to null.
20934
20956
 
20935
- Return ONLY the summary text, no JSON or formatting.`;
20957
+ Return a JSON object with this exact structure:
20958
+ {"summary": "your 2-3 sentence summary", "progressEstimate": 75}
20959
+
20960
+ Use null for progressEstimate if the comments don't provide enough evidence to estimate.
20961
+ IMPORTANT: Only return the JSON object, no other text.`;
20936
20962
  async function analyzeSingleArtifactComments(artifactId, title, jiraKey, jiraStatus, jiraIssues, linkedJiraIssues, linkedIssueSignals) {
20937
20963
  const promptParts = [];
20938
20964
  const primaryData = jiraIssues.get(jiraKey);
@@ -20955,7 +20981,7 @@ ${commentTexts}`);
20955
20981
  promptParts.push(`### Linked: ${signal.sourceKey} (${signal.linkType})
20956
20982
  ${commentTexts}`);
20957
20983
  }
20958
- if (promptParts.length === 0) return null;
20984
+ if (promptParts.length === 0) return { summary: null, progressEstimate: null };
20959
20985
  const prompt = promptParts.join("\n\n");
20960
20986
  const result = query2({
20961
20987
  prompt,
@@ -20972,11 +20998,25 @@ ${commentTexts}`);
20972
20998
  (b) => b.type === "text"
20973
20999
  );
20974
21000
  if (textBlock) {
20975
- return textBlock.text.trim();
21001
+ return parseCommentAnalysis(textBlock.text.trim());
20976
21002
  }
20977
21003
  }
20978
21004
  }
20979
- return null;
21005
+ return { summary: null, progressEstimate: null };
21006
+ }
21007
+ function parseCommentAnalysis(text) {
21008
+ const parsed = parseLlmJson(text);
21009
+ if (parsed && typeof parsed.summary === "string") {
21010
+ const progressEstimate2 = typeof parsed.progressEstimate === "number" && parsed.progressEstimate >= 0 && parsed.progressEstimate <= 100 ? Math.round(parsed.progressEstimate) : null;
21011
+ return { summary: parsed.summary, progressEstimate: progressEstimate2 };
21012
+ }
21013
+ let progressEstimate = null;
21014
+ const pctMatch = text.match(/(\d{1,3})%/);
21015
+ if (pctMatch) {
21016
+ const pct = parseInt(pctMatch[1], 10);
21017
+ if (pct >= 0 && pct <= 100) progressEstimate = pct;
21018
+ }
21019
+ return { summary: text, progressEstimate };
20980
21020
  }
20981
21021
  function emptyArtifactReport(artifactId, errors) {
20982
21022
  return {
@@ -20996,6 +21036,7 @@ function emptyArtifactReport(artifactId, errors) {
20996
21036
  progressDrift: false,
20997
21037
  commentSignals: [],
20998
21038
  commentSummary: null,
21039
+ commentAnalysisProgress: null,
20999
21040
  linkedIssues: [],
21000
21041
  linkedIssueSignals: [],
21001
21042
  children: [],
@@ -21033,6 +21074,9 @@ function formatArtifactReport(report) {
21033
21074
  if (report.commentSummary) {
21034
21075
  parts.push(`## Comments`);
21035
21076
  parts.push(report.commentSummary);
21077
+ if (report.commentAnalysisProgress !== null) {
21078
+ parts.push(` \u{1F4CA} Comment-derived progress estimate: ${report.commentAnalysisProgress}%`);
21079
+ }
21036
21080
  parts.push("");
21037
21081
  }
21038
21082
  if (report.children.length > 0) {
@@ -21113,6 +21157,12 @@ function formatArtifactChild(parts, child, depth) {
21113
21157
  formatArtifactChild(parts, grandchild, depth + 1);
21114
21158
  }
21115
21159
  }
21160
+ function findLast(arr, predicate) {
21161
+ for (let i = arr.length - 1; i >= 0; i--) {
21162
+ if (predicate(arr[i])) return arr[i];
21163
+ }
21164
+ return void 0;
21165
+ }
21116
21166
 
21117
21167
  // src/skills/builtin/jira/tools.ts
21118
21168
  var JIRA_TYPE = "jira-issue";