mrvn-cli 0.5.20 → 0.5.22

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) {
@@ -19829,6 +19842,7 @@ function generateProposedActions(issues) {
19829
19842
  // src/skills/builtin/jira/sprint-progress.ts
19830
19843
  import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
19831
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"]);
19832
19846
  var BATCH_SIZE = 5;
19833
19847
  var MAX_LINKED_ISSUES = 50;
19834
19848
  var BLOCKED_WEIGHT_RISK_THRESHOLD = 0.3;
@@ -20755,8 +20769,16 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20755
20769
  children.push(childReport);
20756
20770
  }
20757
20771
  if (children.length > 0) {
20772
+ const childProgressValues = children.map((c) => {
20773
+ const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
20774
+ const lastStatus = findLast(updates, (u) => u.field === "status");
20775
+ if (lastStatus && PROGRESS_DONE_STATUSES.has(String(lastStatus.proposedValue))) return 100;
20776
+ const lastProgress = findLast(updates, (u) => u.field === "progress");
20777
+ if (lastProgress) return lastProgress.proposedValue;
20778
+ return c.marvinProgress;
20779
+ });
20758
20780
  const rolledUpProgress = Math.round(
20759
- children.reduce((s, c) => s + c.marvinProgress, 0) / children.length
20781
+ childProgressValues.reduce((s, p) => s + p, 0) / childProgressValues.length
20760
20782
  );
20761
20783
  if (rolledUpProgress !== currentProgress) {
20762
20784
  proposedUpdates.push({
@@ -20771,8 +20793,14 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20771
20793
  const signals = buildSignals(commentSignals, linkedIssues, statusDrift, proposedMarvinStatus);
20772
20794
  const appliedUpdates = [];
20773
20795
  if (options.applyUpdates && proposedUpdates.length > 0) {
20796
+ const doneArtifacts = new Set(
20797
+ proposedUpdates.filter((u) => u.field === "status" && PROGRESS_DONE_STATUSES.has(String(u.proposedValue))).map((u) => u.artifactId)
20798
+ );
20774
20799
  for (const update of proposedUpdates) {
20775
20800
  if (update.field === "review") continue;
20801
+ if (update.field === "progress" && doneArtifacts.has(update.artifactId)) {
20802
+ if (update.proposedValue !== 100) continue;
20803
+ }
20776
20804
  try {
20777
20805
  const updatePayload = {
20778
20806
  [update.field]: update.proposedValue,
@@ -20782,12 +20810,14 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
20782
20810
  updatePayload.progressOverride = false;
20783
20811
  }
20784
20812
  store.update(update.artifactId, updatePayload);
20785
- const updatedDoc = store.get(update.artifactId);
20786
- if (updatedDoc) {
20787
- if (updatedDoc.frontmatter.type === "task") {
20788
- propagateProgressFromTask(store, update.artifactId);
20789
- } else if (updatedDoc.frontmatter.type === "action") {
20790
- propagateProgressToAction(store, update.artifactId);
20813
+ if (update.field === "status") {
20814
+ const updatedDoc = store.get(update.artifactId);
20815
+ if (updatedDoc) {
20816
+ if (updatedDoc.frontmatter.type === "task") {
20817
+ propagateProgressFromTask(store, update.artifactId);
20818
+ } else if (updatedDoc.frontmatter.type === "action") {
20819
+ propagateProgressToAction(store, update.artifactId);
20820
+ }
20791
20821
  }
20792
20822
  }
20793
20823
  appliedUpdates.push(update);
@@ -21092,6 +21122,12 @@ function formatArtifactChild(parts, child, depth) {
21092
21122
  formatArtifactChild(parts, grandchild, depth + 1);
21093
21123
  }
21094
21124
  }
21125
+ function findLast(arr, predicate) {
21126
+ for (let i = arr.length - 1; i >= 0; i--) {
21127
+ if (predicate(arr[i])) return arr[i];
21128
+ }
21129
+ return void 0;
21130
+ }
21095
21131
 
21096
21132
  // src/skills/builtin/jira/tools.ts
21097
21133
  var JIRA_TYPE = "jira-issue";