mrvn-cli 0.5.26 → 0.5.27

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/dist/index.js CHANGED
@@ -21118,7 +21118,10 @@ function normalizeEntry(entry) {
21118
21118
  childCount: typeof entry.childCount === "number" ? entry.childCount : 0,
21119
21119
  childDoneCount: typeof entry.childDoneCount === "number" ? entry.childDoneCount : 0,
21120
21120
  childRollupProgress: typeof entry.childRollupProgress === "number" ? entry.childRollupProgress : null,
21121
- linkedIssueCount: typeof entry.linkedIssueCount === "number" ? entry.linkedIssueCount : 0
21121
+ linkedIssueCount: typeof entry.linkedIssueCount === "number" ? entry.linkedIssueCount : 0,
21122
+ blockerProgress: typeof entry.blockerProgress === "number" ? entry.blockerProgress : null,
21123
+ totalBlockers: typeof entry.totalBlockers === "number" ? entry.totalBlockers : 0,
21124
+ resolvedBlockers: typeof entry.resolvedBlockers === "number" ? entry.resolvedBlockers : 0
21122
21125
  };
21123
21126
  }
21124
21127
  function renderAssessmentTimeline(history) {
@@ -21138,6 +21141,10 @@ function renderAssessmentTimeline(history) {
21138
21141
  const bar = progressBarHtml(entry.childRollupProgress ?? 0);
21139
21142
  parts.push(`<div class="assessment-stat">\u{1F476} Children: ${entry.childDoneCount}/${entry.childCount} done ${bar} ${entry.childRollupProgress ?? 0}%</div>`);
21140
21143
  }
21144
+ if (entry.totalBlockers > 0) {
21145
+ const bar = progressBarHtml(entry.blockerProgress ?? 0);
21146
+ parts.push(`<div class="assessment-stat">\u{1F6A7} Blockers: ${entry.resolvedBlockers}/${entry.totalBlockers} resolved ${bar} ${entry.blockerProgress ?? 0}%</div>`);
21147
+ }
21141
21148
  if (entry.linkedIssueCount > 0) {
21142
21149
  parts.push(`<div class="assessment-stat">\u{1F517} Linked issues: ${entry.linkedIssueCount}</div>`);
21143
21150
  }
@@ -27028,6 +27035,15 @@ function analyzeLinkedIssueSignals(linkedIssues, frontmatter, jiraKey, proposedU
27028
27035
  });
27029
27036
  }
27030
27037
  }
27038
+ function computeBlockerProgress(linkedIssues, prerequisiteWeight) {
27039
+ const blockerLinks = linkedIssues.filter(
27040
+ (l) => BLOCKER_LINK_PATTERNS.some((p) => l.relationship.toLowerCase().includes(p.split(" ")[0]))
27041
+ );
27042
+ if (blockerLinks.length === 0) return null;
27043
+ const resolved = blockerLinks.filter((l) => l.isDone).length;
27044
+ const blockerProgress = Math.round(resolved / blockerLinks.length * prerequisiteWeight * 100);
27045
+ return { blockerProgress, totalBlockers: blockerLinks.length, resolvedBlockers: resolved };
27046
+ }
27031
27047
  var LINKED_COMMENT_ANALYSIS_PROMPT = `You are a delivery management assistant analyzing Jira comments from linked issues for progress signals.
27032
27048
 
27033
27049
  For each linked issue below, read the comments and produce a 1-sentence summary focused on: impact on the parent issue, blockers, or decisions.
@@ -27441,6 +27457,39 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
27441
27457
  });
27442
27458
  }
27443
27459
  }
27460
+ const prerequisiteWeight = options.prerequisiteWeight ?? 0.3;
27461
+ const blockerResult = computeBlockerProgress(linkedIssues, prerequisiteWeight);
27462
+ let blockerProgressValue = null;
27463
+ let totalBlockersCount = 0;
27464
+ let resolvedBlockersCount = 0;
27465
+ if (blockerResult && !fm.progressOverride && !DONE_STATUSES15.has(fm.status)) {
27466
+ blockerProgressValue = blockerResult.blockerProgress;
27467
+ totalBlockersCount = blockerResult.totalBlockers;
27468
+ resolvedBlockersCount = blockerResult.resolvedBlockers;
27469
+ const lastProgressUpdate = findLast(proposedUpdates, (u) => u.artifactId === fm.id && u.field === "progress");
27470
+ const implementationProgress = lastProgressUpdate ? lastProgressUpdate.proposedValue : currentProgress;
27471
+ const combinedProgress = Math.round(
27472
+ blockerResult.blockerProgress + implementationProgress * (1 - prerequisiteWeight)
27473
+ );
27474
+ const estimatedProgress = Math.max(currentProgress, combinedProgress);
27475
+ if (estimatedProgress !== currentProgress && estimatedProgress !== implementationProgress) {
27476
+ for (let i = proposedUpdates.length - 1; i >= 0; i--) {
27477
+ if (proposedUpdates[i].artifactId === fm.id && proposedUpdates[i].field === "progress") {
27478
+ proposedUpdates.splice(i, 1);
27479
+ }
27480
+ }
27481
+ proposedUpdates.push({
27482
+ artifactId: fm.id,
27483
+ field: "progress",
27484
+ currentValue: currentProgress,
27485
+ proposedValue: estimatedProgress,
27486
+ reason: `Blocker resolution (${resolvedBlockersCount}/${totalBlockersCount}) + implementation \u2192 dependency-weighted progress ${estimatedProgress}%`
27487
+ });
27488
+ }
27489
+ } else if (blockerResult) {
27490
+ totalBlockersCount = blockerResult.totalBlockers;
27491
+ resolvedBlockersCount = blockerResult.resolvedBlockers;
27492
+ }
27444
27493
  const signals = buildSignals(commentSignals, linkedIssues, statusDrift, proposedMarvinStatus);
27445
27494
  const appliedUpdates = [];
27446
27495
  if (options.applyUpdates && proposedUpdates.length > 0) {
@@ -27483,7 +27532,10 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
27483
27532
  commentAnalysisProgress,
27484
27533
  signals,
27485
27534
  children,
27486
- linkedIssues
27535
+ linkedIssues,
27536
+ blockerProgressValue,
27537
+ totalBlockersCount,
27538
+ resolvedBlockersCount
27487
27539
  );
27488
27540
  const existingHistory = Array.isArray(fm.assessmentHistory) ? fm.assessmentHistory : [];
27489
27541
  const legacySummary = fm.assessmentSummary;
@@ -27532,6 +27584,9 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
27532
27584
  commentAnalysisProgress,
27533
27585
  linkedIssues,
27534
27586
  linkedIssueSignals,
27587
+ blockerProgress: blockerProgressValue,
27588
+ totalBlockers: totalBlockersCount,
27589
+ resolvedBlockers: resolvedBlockersCount,
27535
27590
  children,
27536
27591
  proposedUpdates: options.applyUpdates ? [] : proposedUpdates,
27537
27592
  appliedUpdates,
@@ -27711,6 +27766,9 @@ function emptyArtifactReport(artifactId, errors) {
27711
27766
  commentAnalysisProgress: null,
27712
27767
  linkedIssues: [],
27713
27768
  linkedIssueSignals: [],
27769
+ blockerProgress: null,
27770
+ totalBlockers: 0,
27771
+ resolvedBlockers: 0,
27714
27772
  children: [],
27715
27773
  proposedUpdates: [],
27716
27774
  appliedUpdates: [],
@@ -27718,7 +27776,7 @@ function emptyArtifactReport(artifactId, errors) {
27718
27776
  errors
27719
27777
  };
27720
27778
  }
27721
- function buildAssessmentSummary(commentSummary, commentAnalysisProgress, signals, children, linkedIssues) {
27779
+ function buildAssessmentSummary(commentSummary, commentAnalysisProgress, signals, children, linkedIssues, blockerProgress = null, totalBlockers = 0, resolvedBlockers = 0) {
27722
27780
  const childProgressValues = children.map((c) => {
27723
27781
  const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
27724
27782
  const lastStatus = findLast(updates, (u) => u.field === "status");
@@ -27742,7 +27800,10 @@ function buildAssessmentSummary(commentSummary, commentAnalysisProgress, signals
27742
27800
  childCount: children.length,
27743
27801
  childDoneCount,
27744
27802
  childRollupProgress,
27745
- linkedIssueCount: linkedIssues.length
27803
+ linkedIssueCount: linkedIssues.length,
27804
+ blockerProgress,
27805
+ totalBlockers,
27806
+ resolvedBlockers
27746
27807
  };
27747
27808
  }
27748
27809
  function formatArtifactReport(report) {
@@ -27778,6 +27839,12 @@ function formatArtifactReport(report) {
27778
27839
  }
27779
27840
  parts.push("");
27780
27841
  }
27842
+ if (report.totalBlockers > 0) {
27843
+ parts.push(`## Blocker Resolution`);
27844
+ const bpLabel = report.blockerProgress !== null ? `${report.blockerProgress}%` : "n/a (skipped)";
27845
+ parts.push(` ${report.resolvedBlockers}/${report.totalBlockers} blockers resolved \u2192 ${bpLabel} prerequisite progress`);
27846
+ parts.push("");
27847
+ }
27781
27848
  if (report.children.length > 0) {
27782
27849
  const doneCount = report.children.filter((c) => DONE_STATUSES15.has(c.marvinStatus)).length;
27783
27850
  const childProgress = Math.round(
@@ -28676,10 +28743,11 @@ function createJiraTools(store, projectConfig) {
28676
28743
  // --- Single-artifact assessment ---
28677
28744
  tool20(
28678
28745
  "assess_artifact",
28679
- "Deep assessment of a single Marvin artifact (task, action, or epic). Fetches live Jira status, analyzes comments with LLM, traverses all linked issues, detects drift, rolls up child progress, and extracts contextual signals (blockers, unblocks, handoffs, superseded work).",
28746
+ "Deep assessment of a single Marvin artifact (task, action, or epic). Fetches live Jira status, analyzes comments with LLM, traverses all linked issues, detects drift, rolls up child progress, computes dependency-weighted progress from blocker resolution, and extracts contextual signals (blockers, unblocks, handoffs, superseded work).",
28680
28747
  {
28681
28748
  artifactId: external_exports.string().describe("Marvin artifact ID (e.g. 'T-063', 'A-151', 'E-003')"),
28682
- applyUpdates: external_exports.boolean().optional().describe("Apply proposed status/progress updates to the artifact (default false)")
28749
+ applyUpdates: external_exports.boolean().optional().describe("Apply proposed status/progress updates to the artifact (default false)"),
28750
+ prerequisiteWeight: external_exports.number().min(0).max(1).optional().describe("Weight for blocker-resolution progress signal (0-1, default 0.3). Portion of effort attributed to dependency readiness.")
28683
28751
  },
28684
28752
  async (args) => {
28685
28753
  const jira = createJiraClient(jiraUserConfig);
@@ -28691,6 +28759,7 @@ function createJiraTools(store, projectConfig) {
28691
28759
  {
28692
28760
  artifactId: args.artifactId,
28693
28761
  applyUpdates: args.applyUpdates ?? false,
28762
+ prerequisiteWeight: args.prerequisiteWeight,
28694
28763
  statusMap
28695
28764
  }
28696
28765
  );
@@ -34359,7 +34428,7 @@ function createProgram() {
34359
34428
  const program = new Command();
34360
34429
  program.name("marvin").description(
34361
34430
  "AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
34362
- ).version("0.5.26");
34431
+ ).version("0.5.27");
34363
34432
  program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
34364
34433
  await initCommand();
34365
34434
  });