mrvn-cli 0.5.22 → 0.5.24

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.
@@ -20732,11 +20732,12 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20732
20732
  }
20733
20733
  }
20734
20734
  const primaryHasComments = jiraKey ? (jiraIssues.get(jiraKey)?.comments.length ?? 0) > 0 : false;
20735
+ let commentAnalysisProgress = null;
20735
20736
  if (depth < MAX_LLM_DEPTH && jiraKey && primaryHasComments) {
20736
20737
  const estimatedChars = estimateCommentTextSize(jiraIssues, linkedJiraIssues, linkedIssueSignals);
20737
20738
  if (estimatedChars <= MAX_LLM_COMMENT_CHARS) {
20738
20739
  try {
20739
- const summary = await analyzeSingleArtifactComments(
20740
+ const analysis = await analyzeSingleArtifactComments(
20740
20741
  fm.id,
20741
20742
  fm.title,
20742
20743
  jiraKey,
@@ -20745,7 +20746,20 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20745
20746
  linkedJiraIssues,
20746
20747
  linkedIssueSignals
20747
20748
  );
20748
- 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
+ }
20749
20763
  } catch (err) {
20750
20764
  errors.push(`Comment analysis failed: ${err instanceof Error ? err.message : String(err)}`);
20751
20765
  }
@@ -20826,6 +20840,23 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20826
20840
  }
20827
20841
  }
20828
20842
  }
20843
+ if (options.applyUpdates) {
20844
+ const assessmentSummary = buildAssessmentSummary(
20845
+ commentSummary,
20846
+ commentAnalysisProgress,
20847
+ signals,
20848
+ children,
20849
+ linkedIssues
20850
+ );
20851
+ try {
20852
+ store.update(fm.id, {
20853
+ assessmentSummary,
20854
+ lastAssessedAt: assessmentSummary.generatedAt
20855
+ });
20856
+ } catch (err) {
20857
+ errors.push(`Failed to persist assessment summary: ${err instanceof Error ? err.message : String(err)}`);
20858
+ }
20859
+ }
20829
20860
  return {
20830
20861
  artifactId: fm.id,
20831
20862
  title: fm.title,
@@ -20843,6 +20874,7 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20843
20874
  progressDrift,
20844
20875
  commentSignals,
20845
20876
  commentSummary,
20877
+ commentAnalysisProgress,
20846
20878
  linkedIssues,
20847
20879
  linkedIssueSignals,
20848
20880
  children,
@@ -20935,13 +20967,15 @@ function estimateCommentTextSize(jiraIssues, linkedJiraIssues, linkedIssueSignal
20935
20967
  }
20936
20968
  var SINGLE_ARTIFACT_COMMENT_PROMPT = `You are a delivery management assistant analyzing Jira comments for a single work item.
20937
20969
 
20938
- Produce a 2-3 sentence progress summary covering:
20939
- - What work has been completed
20940
- - What is pending or blocked
20941
- - Any decisions, handoffs, or scheduling mentioned
20942
- - Relevant context from linked issue comments (if provided)
20970
+ Analyze the comments and produce:
20971
+ 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).
20972
+ 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.
20943
20973
 
20944
- Return ONLY the summary text, no JSON or formatting.`;
20974
+ Return a JSON object with this exact structure:
20975
+ {"summary": "your 2-3 sentence summary", "progressEstimate": 75}
20976
+
20977
+ Use null for progressEstimate if the comments don't provide enough evidence to estimate.
20978
+ IMPORTANT: Only return the JSON object, no other text.`;
20945
20979
  async function analyzeSingleArtifactComments(artifactId, title, jiraKey, jiraStatus, jiraIssues, linkedJiraIssues, linkedIssueSignals) {
20946
20980
  const promptParts = [];
20947
20981
  const primaryData = jiraIssues.get(jiraKey);
@@ -20964,7 +20998,7 @@ ${commentTexts}`);
20964
20998
  promptParts.push(`### Linked: ${signal.sourceKey} (${signal.linkType})
20965
20999
  ${commentTexts}`);
20966
21000
  }
20967
- if (promptParts.length === 0) return null;
21001
+ if (promptParts.length === 0) return { summary: null, progressEstimate: null };
20968
21002
  const prompt = promptParts.join("\n\n");
20969
21003
  const result = query2({
20970
21004
  prompt,
@@ -20981,11 +21015,25 @@ ${commentTexts}`);
20981
21015
  (b) => b.type === "text"
20982
21016
  );
20983
21017
  if (textBlock) {
20984
- return textBlock.text.trim();
21018
+ return parseCommentAnalysis(textBlock.text.trim());
20985
21019
  }
20986
21020
  }
20987
21021
  }
20988
- return null;
21022
+ return { summary: null, progressEstimate: null };
21023
+ }
21024
+ function parseCommentAnalysis(text) {
21025
+ const parsed = parseLlmJson(text);
21026
+ if (parsed && typeof parsed.summary === "string") {
21027
+ const progressEstimate2 = typeof parsed.progressEstimate === "number" && parsed.progressEstimate >= 0 && parsed.progressEstimate <= 100 ? Math.round(parsed.progressEstimate) : null;
21028
+ return { summary: parsed.summary, progressEstimate: progressEstimate2 };
21029
+ }
21030
+ let progressEstimate = null;
21031
+ const pctMatch = text.match(/(\d{1,3})%/);
21032
+ if (pctMatch) {
21033
+ const pct = parseInt(pctMatch[1], 10);
21034
+ if (pct >= 0 && pct <= 100) progressEstimate = pct;
21035
+ }
21036
+ return { summary: text, progressEstimate };
20989
21037
  }
20990
21038
  function emptyArtifactReport(artifactId, errors) {
20991
21039
  return {
@@ -21005,6 +21053,7 @@ function emptyArtifactReport(artifactId, errors) {
21005
21053
  progressDrift: false,
21006
21054
  commentSignals: [],
21007
21055
  commentSummary: null,
21056
+ commentAnalysisProgress: null,
21008
21057
  linkedIssues: [],
21009
21058
  linkedIssueSignals: [],
21010
21059
  children: [],
@@ -21014,6 +21063,33 @@ function emptyArtifactReport(artifactId, errors) {
21014
21063
  errors
21015
21064
  };
21016
21065
  }
21066
+ function buildAssessmentSummary(commentSummary, commentAnalysisProgress, signals, children, linkedIssues) {
21067
+ const childProgressValues = children.map((c) => {
21068
+ const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
21069
+ const lastStatus = findLast(updates, (u) => u.field === "status");
21070
+ if (lastStatus && PROGRESS_DONE_STATUSES.has(String(lastStatus.proposedValue))) return 100;
21071
+ const lastProgress = findLast(updates, (u) => u.field === "progress");
21072
+ if (lastProgress) return lastProgress.proposedValue;
21073
+ return c.marvinProgress;
21074
+ });
21075
+ const childDoneCount = children.filter((c, i) => {
21076
+ const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
21077
+ const lastStatus = findLast(updates, (u) => u.field === "status");
21078
+ const effectiveStatus = lastStatus ? String(lastStatus.proposedValue) : c.marvinStatus;
21079
+ return DONE_STATUSES6.has(effectiveStatus);
21080
+ }).length;
21081
+ const childRollupProgress = children.length > 0 ? Math.round(childProgressValues.reduce((s, p) => s + p, 0) / childProgressValues.length) : null;
21082
+ return {
21083
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
21084
+ commentSummary,
21085
+ commentAnalysisProgress,
21086
+ signals,
21087
+ childCount: children.length,
21088
+ childDoneCount,
21089
+ childRollupProgress,
21090
+ linkedIssueCount: linkedIssues.length
21091
+ };
21092
+ }
21017
21093
  function formatArtifactReport(report) {
21018
21094
  const parts = [];
21019
21095
  parts.push(`# Artifact Assessment \u2014 ${report.artifactId}`);
@@ -21042,6 +21118,9 @@ function formatArtifactReport(report) {
21042
21118
  if (report.commentSummary) {
21043
21119
  parts.push(`## Comments`);
21044
21120
  parts.push(report.commentSummary);
21121
+ if (report.commentAnalysisProgress !== null) {
21122
+ parts.push(` \u{1F4CA} Comment-derived progress estimate: ${report.commentAnalysisProgress}%`);
21123
+ }
21045
21124
  parts.push("");
21046
21125
  }
21047
21126
  if (report.children.length > 0) {