mrvn-cli 0.5.19 → 0.5.21

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.
@@ -14369,24 +14369,26 @@ function getEffectiveProgress(frontmatter) {
14369
14369
  if (typeof raw === "number") return Math.max(0, Math.min(100, Math.round(raw)));
14370
14370
  return STATUS_PROGRESS_DEFAULTS[frontmatter.status] ?? 0;
14371
14371
  }
14372
- function propagateProgressFromTask(store, taskId) {
14372
+ function propagateProgressFromTask(store, taskId, options) {
14373
14373
  const updated = [];
14374
14374
  const task = store.get(taskId);
14375
14375
  if (!task) return updated;
14376
- if (DONE_STATUSES.has(task.frontmatter.status)) {
14377
- if (task.frontmatter.progress !== 100) {
14378
- store.update(taskId, { progress: 100 });
14379
- updated.push(taskId);
14380
- }
14381
- } else if (!task.frontmatter.progressOverride) {
14382
- const children = store.list({ type: "contribution" }).filter((d) => d.frontmatter.aboutArtifact === taskId);
14383
- if (children.length > 0) {
14384
- const avg = children.reduce((sum, c) => sum + getEffectiveProgress(c.frontmatter), 0) / children.length;
14385
- const progress = Math.round(avg);
14386
- if (task.frontmatter.progress !== progress) {
14387
- store.update(taskId, { progress });
14376
+ if (!options?.skipSelf) {
14377
+ if (DONE_STATUSES.has(task.frontmatter.status)) {
14378
+ if (task.frontmatter.progress !== 100) {
14379
+ store.update(taskId, { progress: 100 });
14388
14380
  updated.push(taskId);
14389
14381
  }
14382
+ } else if (!task.frontmatter.progressOverride) {
14383
+ const children = store.list({ type: "contribution" }).filter((d) => d.frontmatter.aboutArtifact === taskId);
14384
+ if (children.length > 0) {
14385
+ const avg = children.reduce((sum, c) => sum + getEffectiveProgress(c.frontmatter), 0) / children.length;
14386
+ const progress = Math.round(avg);
14387
+ if (task.frontmatter.progress !== progress) {
14388
+ store.update(taskId, { progress });
14389
+ updated.push(taskId);
14390
+ }
14391
+ }
14390
14392
  }
14391
14393
  }
14392
14394
  const aboutArtifact = task.frontmatter.aboutArtifact;
@@ -14398,10 +14400,11 @@ function propagateProgressFromTask(store, taskId) {
14398
14400
  }
14399
14401
  return updated;
14400
14402
  }
14401
- function propagateProgressToAction(store, actionId) {
14403
+ function propagateProgressToAction(store, actionId, options) {
14402
14404
  const updated = [];
14403
14405
  const action = store.get(actionId);
14404
14406
  if (!action) return updated;
14407
+ if (options?.skipSelf) return updated;
14405
14408
  if (DONE_STATUSES.has(action.frontmatter.status)) {
14406
14409
  if (action.frontmatter.progress !== 100) {
14407
14410
  store.update(actionId, { progress: 100 });
@@ -14586,10 +14589,11 @@ function createActionTools(store) {
14586
14589
  tags: external_exports.array(external_exports.string()).optional().describe("Replace all tags. When provided with sprints, sprint tags are merged into this array."),
14587
14590
  sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (replaces existing sprint tags). E.g. ['SP-001']."),
14588
14591
  workFocus: external_exports.string().optional().describe("Work focus name (e.g. 'Budget UX'). Replaces existing focus:<value> tag."),
14589
- progress: external_exports.number().nullable().optional().describe("Explicit progress percentage (0-100). Pass null to clear the override and revert to auto-calculation from children.")
14592
+ progress: external_exports.number().nullable().optional().describe("Explicit progress percentage (0-100). Pass null to clear the override and revert to auto-calculation from children."),
14593
+ progressOverride: external_exports.boolean().optional().describe("Control auto-calculation lock. true = lock progress to explicit value, false = allow auto-calculation from children. When omitted: setting progress implies true, null progress implies false.")
14590
14594
  },
14591
14595
  async (args) => {
14592
- const { id, content, sprints, tags, workFocus, progress, owner, assignee, ...updates } = args;
14596
+ const { id, content, sprints, tags, workFocus, progress, progressOverride, owner, assignee, ...updates } = args;
14593
14597
  if (owner !== void 0) updates.owner = normalizeOwner(owner);
14594
14598
  if (assignee !== void 0) updates.assignee = assignee;
14595
14599
  if (tags !== void 0) {
@@ -14628,13 +14632,17 @@ function createActionTools(store) {
14628
14632
  }
14629
14633
  if (typeof progress === "number") {
14630
14634
  updates.progress = Math.max(0, Math.min(100, Math.round(progress)));
14631
- updates.progressOverride = true;
14635
+ updates.progressOverride = progressOverride ?? true;
14632
14636
  } else if (progress === null) {
14633
14637
  updates.progressOverride = false;
14638
+ } else if (progressOverride !== void 0) {
14639
+ updates.progressOverride = progressOverride;
14634
14640
  }
14635
14641
  const doc = store.update(id, updates, content);
14636
14642
  if (args.status !== void 0 || typeof progress === "number") {
14637
- propagateProgressToAction(store, id);
14643
+ propagateProgressToAction(store, id, {
14644
+ skipSelf: typeof progress === "number"
14645
+ });
14638
14646
  }
14639
14647
  return {
14640
14648
  content: [
@@ -17867,10 +17875,11 @@ function createTaskTools(store) {
17867
17875
  priority: external_exports.enum(["critical", "high", "medium", "low"]).optional().describe("New priority"),
17868
17876
  tags: external_exports.array(external_exports.string()).optional().describe("Replace tags (e.g. remove old tags, add new ones)"),
17869
17877
  workFocus: external_exports.string().optional().describe("Work focus name (e.g. 'Budget UX'). Replaces existing focus:<value> tag."),
17870
- progress: external_exports.number().nullable().optional().describe("Explicit progress percentage (0-100). Overrides auto-calculation from child contributions. Pass null to clear the override and revert to auto-calculation.")
17878
+ progress: external_exports.number().nullable().optional().describe("Explicit progress percentage (0-100). Overrides auto-calculation from child contributions. Pass null to clear the override and revert to auto-calculation."),
17879
+ progressOverride: external_exports.boolean().optional().describe("Control auto-calculation lock. true = lock progress to explicit value, false = allow auto-calculation from children. When omitted: setting progress implies true, null progress implies false.")
17871
17880
  },
17872
17881
  async (args) => {
17873
- const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, workFocus, progress, ...updates } = args;
17882
+ const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, workFocus, progress, progressOverride, ...updates } = args;
17874
17883
  const warnings = [];
17875
17884
  if (rawLinkedEpic !== void 0) {
17876
17885
  const linkedEpics = normalizeLinkedEpics(rawLinkedEpic);
@@ -17899,13 +17908,17 @@ function createTaskTools(store) {
17899
17908
  }
17900
17909
  if (typeof progress === "number") {
17901
17910
  updates.progress = Math.max(0, Math.min(100, Math.round(progress)));
17902
- updates.progressOverride = true;
17911
+ updates.progressOverride = progressOverride ?? true;
17903
17912
  } else if (progress === null) {
17904
17913
  updates.progressOverride = false;
17914
+ } else if (progressOverride !== void 0) {
17915
+ updates.progressOverride = progressOverride;
17905
17916
  }
17906
17917
  const doc = store.update(id, updates, content);
17907
17918
  if (args.status !== void 0 || typeof progress === "number") {
17908
- propagateProgressFromTask(store, id);
17919
+ propagateProgressFromTask(store, id, {
17920
+ skipSelf: typeof progress === "number"
17921
+ });
17909
17922
  }
17910
17923
  const parts = [`Updated task ${doc.frontmatter.id}: ${doc.frontmatter.title}`];
17911
17924
  if (warnings.length > 0) {
@@ -20755,11 +20768,8 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20755
20768
  children.push(childReport);
20756
20769
  }
20757
20770
  if (children.length > 0) {
20758
- const rolledUpProgress = computeWeightedProgress(
20759
- children.map((c) => ({
20760
- weight: resolveWeight(void 0).weight,
20761
- progress: c.marvinProgress
20762
- }))
20771
+ const rolledUpProgress = Math.round(
20772
+ children.reduce((s, c) => s + c.marvinProgress, 0) / children.length
20763
20773
  );
20764
20774
  if (rolledUpProgress !== currentProgress) {
20765
20775
  proposedUpdates.push({
@@ -20767,15 +20777,21 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20767
20777
  field: "progress",
20768
20778
  currentValue: currentProgress,
20769
20779
  proposedValue: rolledUpProgress,
20770
- reason: `Rolled up from ${children.length} children (weighted average ${rolledUpProgress}%)`
20780
+ reason: `Rolled up from ${children.length} children (average ${rolledUpProgress}%)`
20771
20781
  });
20772
20782
  }
20773
20783
  }
20774
20784
  const signals = buildSignals(commentSignals, linkedIssues, statusDrift, proposedMarvinStatus);
20775
20785
  const appliedUpdates = [];
20776
20786
  if (options.applyUpdates && proposedUpdates.length > 0) {
20787
+ const doneArtifacts = new Set(
20788
+ proposedUpdates.filter((u) => u.field === "status" && DONE_STATUSES6.has(String(u.proposedValue))).map((u) => u.artifactId)
20789
+ );
20777
20790
  for (const update of proposedUpdates) {
20778
20791
  if (update.field === "review") continue;
20792
+ if (update.field === "progress" && doneArtifacts.has(update.artifactId)) {
20793
+ if (update.proposedValue !== 100) continue;
20794
+ }
20779
20795
  try {
20780
20796
  const updatePayload = {
20781
20797
  [update.field]: update.proposedValue,
@@ -20785,12 +20801,14 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20785
20801
  updatePayload.progressOverride = false;
20786
20802
  }
20787
20803
  store.update(update.artifactId, updatePayload);
20788
- const updatedDoc = store.get(update.artifactId);
20789
- if (updatedDoc) {
20790
- if (updatedDoc.frontmatter.type === "task") {
20791
- propagateProgressFromTask(store, update.artifactId);
20792
- } else if (updatedDoc.frontmatter.type === "action") {
20793
- propagateProgressToAction(store, update.artifactId);
20804
+ if (update.field === "status") {
20805
+ const updatedDoc = store.get(update.artifactId);
20806
+ if (updatedDoc) {
20807
+ if (updatedDoc.frontmatter.type === "task") {
20808
+ propagateProgressFromTask(store, update.artifactId);
20809
+ } else if (updatedDoc.frontmatter.type === "action") {
20810
+ propagateProgressToAction(store, update.artifactId);
20811
+ }
20794
20812
  }
20795
20813
  }
20796
20814
  appliedUpdates.push(update);
@@ -21019,11 +21037,9 @@ function formatArtifactReport(report) {
21019
21037
  }
21020
21038
  if (report.children.length > 0) {
21021
21039
  const doneCount = report.children.filter((c) => DONE_STATUSES6.has(c.marvinStatus)).length;
21022
- const childWeights = report.children.map((c) => {
21023
- const { weight } = resolveWeight(void 0);
21024
- return { weight, progress: c.marvinProgress };
21025
- });
21026
- const childProgress = childWeights.length > 0 ? Math.round(childWeights.reduce((s, c) => s + c.weight * c.progress, 0) / childWeights.reduce((s, c) => s + c.weight, 0)) : 0;
21040
+ const childProgress = Math.round(
21041
+ report.children.reduce((s, c) => s + c.marvinProgress, 0) / report.children.length
21042
+ );
21027
21043
  const bar = progressBar(childProgress);
21028
21044
  parts.push(`## Children (${doneCount}/${report.children.length} done) ${bar} ${childProgress}%`);
21029
21045
  for (const child of report.children) {