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.
package/dist/marvin.js CHANGED
@@ -14517,24 +14517,26 @@ function getEffectiveProgress(frontmatter) {
14517
14517
  if (typeof raw === "number") return Math.max(0, Math.min(100, Math.round(raw)));
14518
14518
  return STATUS_PROGRESS_DEFAULTS[frontmatter.status] ?? 0;
14519
14519
  }
14520
- function propagateProgressFromTask(store, taskId) {
14520
+ function propagateProgressFromTask(store, taskId, options) {
14521
14521
  const updated = [];
14522
14522
  const task = store.get(taskId);
14523
14523
  if (!task) return updated;
14524
- if (DONE_STATUSES.has(task.frontmatter.status)) {
14525
- if (task.frontmatter.progress !== 100) {
14526
- store.update(taskId, { progress: 100 });
14527
- updated.push(taskId);
14528
- }
14529
- } else if (!task.frontmatter.progressOverride) {
14530
- const children = store.list({ type: "contribution" }).filter((d) => d.frontmatter.aboutArtifact === taskId);
14531
- if (children.length > 0) {
14532
- const avg = children.reduce((sum, c) => sum + getEffectiveProgress(c.frontmatter), 0) / children.length;
14533
- const progress = Math.round(avg);
14534
- if (task.frontmatter.progress !== progress) {
14535
- store.update(taskId, { progress });
14524
+ if (!options?.skipSelf) {
14525
+ if (DONE_STATUSES.has(task.frontmatter.status)) {
14526
+ if (task.frontmatter.progress !== 100) {
14527
+ store.update(taskId, { progress: 100 });
14536
14528
  updated.push(taskId);
14537
14529
  }
14530
+ } else if (!task.frontmatter.progressOverride) {
14531
+ const children = store.list({ type: "contribution" }).filter((d) => d.frontmatter.aboutArtifact === taskId);
14532
+ if (children.length > 0) {
14533
+ const avg = children.reduce((sum, c) => sum + getEffectiveProgress(c.frontmatter), 0) / children.length;
14534
+ const progress = Math.round(avg);
14535
+ if (task.frontmatter.progress !== progress) {
14536
+ store.update(taskId, { progress });
14537
+ updated.push(taskId);
14538
+ }
14539
+ }
14538
14540
  }
14539
14541
  }
14540
14542
  const aboutArtifact = task.frontmatter.aboutArtifact;
@@ -14546,10 +14548,11 @@ function propagateProgressFromTask(store, taskId) {
14546
14548
  }
14547
14549
  return updated;
14548
14550
  }
14549
- function propagateProgressToAction(store, actionId) {
14551
+ function propagateProgressToAction(store, actionId, options) {
14550
14552
  const updated = [];
14551
14553
  const action = store.get(actionId);
14552
14554
  if (!action) return updated;
14555
+ if (options?.skipSelf) return updated;
14553
14556
  if (DONE_STATUSES.has(action.frontmatter.status)) {
14554
14557
  if (action.frontmatter.progress !== 100) {
14555
14558
  store.update(actionId, { progress: 100 });
@@ -16824,10 +16827,11 @@ function createTaskTools(store) {
16824
16827
  priority: external_exports.enum(["critical", "high", "medium", "low"]).optional().describe("New priority"),
16825
16828
  tags: external_exports.array(external_exports.string()).optional().describe("Replace tags (e.g. remove old tags, add new ones)"),
16826
16829
  workFocus: external_exports.string().optional().describe("Work focus name (e.g. 'Budget UX'). Replaces existing focus:<value> tag."),
16827
- 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.")
16830
+ 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."),
16831
+ 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.")
16828
16832
  },
16829
16833
  async (args) => {
16830
- const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, workFocus, progress, ...updates } = args;
16834
+ const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, workFocus, progress, progressOverride, ...updates } = args;
16831
16835
  const warnings = [];
16832
16836
  if (rawLinkedEpic !== void 0) {
16833
16837
  const linkedEpics = normalizeLinkedEpics(rawLinkedEpic);
@@ -16856,13 +16860,17 @@ function createTaskTools(store) {
16856
16860
  }
16857
16861
  if (typeof progress === "number") {
16858
16862
  updates.progress = Math.max(0, Math.min(100, Math.round(progress)));
16859
- updates.progressOverride = true;
16863
+ updates.progressOverride = progressOverride ?? true;
16860
16864
  } else if (progress === null) {
16861
16865
  updates.progressOverride = false;
16866
+ } else if (progressOverride !== void 0) {
16867
+ updates.progressOverride = progressOverride;
16862
16868
  }
16863
16869
  const doc = store.update(id, updates, content);
16864
16870
  if (args.status !== void 0 || typeof progress === "number") {
16865
- propagateProgressFromTask(store, id);
16871
+ propagateProgressFromTask(store, id, {
16872
+ skipSelf: typeof progress === "number"
16873
+ });
16866
16874
  }
16867
16875
  const parts = [`Updated task ${doc.frontmatter.id}: ${doc.frontmatter.title}`];
16868
16876
  if (warnings.length > 0) {
@@ -18915,10 +18923,11 @@ function createActionTools(store) {
18915
18923
  tags: external_exports.array(external_exports.string()).optional().describe("Replace all tags. When provided with sprints, sprint tags are merged into this array."),
18916
18924
  sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (replaces existing sprint tags). E.g. ['SP-001']."),
18917
18925
  workFocus: external_exports.string().optional().describe("Work focus name (e.g. 'Budget UX'). Replaces existing focus:<value> tag."),
18918
- 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.")
18926
+ 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."),
18927
+ 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.")
18919
18928
  },
18920
18929
  async (args) => {
18921
- const { id, content, sprints, tags, workFocus, progress, owner, assignee, ...updates } = args;
18930
+ const { id, content, sprints, tags, workFocus, progress, progressOverride, owner, assignee, ...updates } = args;
18922
18931
  if (owner !== void 0) updates.owner = normalizeOwner(owner);
18923
18932
  if (assignee !== void 0) updates.assignee = assignee;
18924
18933
  if (tags !== void 0) {
@@ -18957,13 +18966,17 @@ function createActionTools(store) {
18957
18966
  }
18958
18967
  if (typeof progress === "number") {
18959
18968
  updates.progress = Math.max(0, Math.min(100, Math.round(progress)));
18960
- updates.progressOverride = true;
18969
+ updates.progressOverride = progressOverride ?? true;
18961
18970
  } else if (progress === null) {
18962
18971
  updates.progressOverride = false;
18972
+ } else if (progressOverride !== void 0) {
18973
+ updates.progressOverride = progressOverride;
18963
18974
  }
18964
18975
  const doc = store.update(id, updates, content);
18965
18976
  if (args.status !== void 0 || typeof progress === "number") {
18966
- propagateProgressToAction(store, id);
18977
+ propagateProgressToAction(store, id, {
18978
+ skipSelf: typeof progress === "number"
18979
+ });
18967
18980
  }
18968
18981
  return {
18969
18982
  content: [
@@ -26966,11 +26979,8 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
26966
26979
  children.push(childReport);
26967
26980
  }
26968
26981
  if (children.length > 0) {
26969
- const rolledUpProgress = computeWeightedProgress(
26970
- children.map((c) => ({
26971
- weight: resolveWeight(void 0).weight,
26972
- progress: c.marvinProgress
26973
- }))
26982
+ const rolledUpProgress = Math.round(
26983
+ children.reduce((s, c) => s + c.marvinProgress, 0) / children.length
26974
26984
  );
26975
26985
  if (rolledUpProgress !== currentProgress) {
26976
26986
  proposedUpdates.push({
@@ -26978,15 +26988,21 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
26978
26988
  field: "progress",
26979
26989
  currentValue: currentProgress,
26980
26990
  proposedValue: rolledUpProgress,
26981
- reason: `Rolled up from ${children.length} children (weighted average ${rolledUpProgress}%)`
26991
+ reason: `Rolled up from ${children.length} children (average ${rolledUpProgress}%)`
26982
26992
  });
26983
26993
  }
26984
26994
  }
26985
26995
  const signals = buildSignals(commentSignals, linkedIssues, statusDrift, proposedMarvinStatus);
26986
26996
  const appliedUpdates = [];
26987
26997
  if (options.applyUpdates && proposedUpdates.length > 0) {
26998
+ const doneArtifacts = new Set(
26999
+ proposedUpdates.filter((u) => u.field === "status" && DONE_STATUSES15.has(String(u.proposedValue))).map((u) => u.artifactId)
27000
+ );
26988
27001
  for (const update of proposedUpdates) {
26989
27002
  if (update.field === "review") continue;
27003
+ if (update.field === "progress" && doneArtifacts.has(update.artifactId)) {
27004
+ if (update.proposedValue !== 100) continue;
27005
+ }
26990
27006
  try {
26991
27007
  const updatePayload = {
26992
27008
  [update.field]: update.proposedValue,
@@ -26996,12 +27012,14 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
26996
27012
  updatePayload.progressOverride = false;
26997
27013
  }
26998
27014
  store.update(update.artifactId, updatePayload);
26999
- const updatedDoc = store.get(update.artifactId);
27000
- if (updatedDoc) {
27001
- if (updatedDoc.frontmatter.type === "task") {
27002
- propagateProgressFromTask(store, update.artifactId);
27003
- } else if (updatedDoc.frontmatter.type === "action") {
27004
- propagateProgressToAction(store, update.artifactId);
27015
+ if (update.field === "status") {
27016
+ const updatedDoc = store.get(update.artifactId);
27017
+ if (updatedDoc) {
27018
+ if (updatedDoc.frontmatter.type === "task") {
27019
+ propagateProgressFromTask(store, update.artifactId);
27020
+ } else if (updatedDoc.frontmatter.type === "action") {
27021
+ propagateProgressToAction(store, update.artifactId);
27022
+ }
27005
27023
  }
27006
27024
  }
27007
27025
  appliedUpdates.push(update);
@@ -27230,11 +27248,9 @@ function formatArtifactReport(report) {
27230
27248
  }
27231
27249
  if (report.children.length > 0) {
27232
27250
  const doneCount = report.children.filter((c) => DONE_STATUSES15.has(c.marvinStatus)).length;
27233
- const childWeights = report.children.map((c) => {
27234
- const { weight } = resolveWeight(void 0);
27235
- return { weight, progress: c.marvinProgress };
27236
- });
27237
- 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;
27251
+ const childProgress = Math.round(
27252
+ report.children.reduce((s, c) => s + c.marvinProgress, 0) / report.children.length
27253
+ );
27238
27254
  const bar = progressBar6(childProgress);
27239
27255
  parts.push(`## Children (${doneCount}/${report.children.length} done) ${bar} ${childProgress}%`);
27240
27256
  for (const child of report.children) {
@@ -33551,7 +33567,7 @@ function createProgram() {
33551
33567
  const program2 = new Command();
33552
33568
  program2.name("marvin").description(
33553
33569
  "AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
33554
- ).version("0.5.19");
33570
+ ).version("0.5.21");
33555
33571
  program2.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
33556
33572
  await initCommand();
33557
33573
  });