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.
- package/dist/index.js +66 -30
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +65 -29
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +66 -30
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14525,24 +14525,26 @@ function getEffectiveProgress(frontmatter) {
|
|
|
14525
14525
|
if (typeof raw === "number") return Math.max(0, Math.min(100, Math.round(raw)));
|
|
14526
14526
|
return STATUS_PROGRESS_DEFAULTS[frontmatter.status] ?? 0;
|
|
14527
14527
|
}
|
|
14528
|
-
function propagateProgressFromTask(store, taskId) {
|
|
14528
|
+
function propagateProgressFromTask(store, taskId, options) {
|
|
14529
14529
|
const updated = [];
|
|
14530
14530
|
const task = store.get(taskId);
|
|
14531
14531
|
if (!task) return updated;
|
|
14532
|
-
if (
|
|
14533
|
-
if (task.frontmatter.
|
|
14534
|
-
|
|
14535
|
-
|
|
14536
|
-
}
|
|
14537
|
-
} else if (!task.frontmatter.progressOverride) {
|
|
14538
|
-
const children = store.list({ type: "contribution" }).filter((d) => d.frontmatter.aboutArtifact === taskId);
|
|
14539
|
-
if (children.length > 0) {
|
|
14540
|
-
const avg = children.reduce((sum, c) => sum + getEffectiveProgress(c.frontmatter), 0) / children.length;
|
|
14541
|
-
const progress = Math.round(avg);
|
|
14542
|
-
if (task.frontmatter.progress !== progress) {
|
|
14543
|
-
store.update(taskId, { progress });
|
|
14532
|
+
if (!options?.skipSelf) {
|
|
14533
|
+
if (DONE_STATUSES.has(task.frontmatter.status)) {
|
|
14534
|
+
if (task.frontmatter.progress !== 100) {
|
|
14535
|
+
store.update(taskId, { progress: 100 });
|
|
14544
14536
|
updated.push(taskId);
|
|
14545
14537
|
}
|
|
14538
|
+
} else if (!task.frontmatter.progressOverride) {
|
|
14539
|
+
const children = store.list({ type: "contribution" }).filter((d) => d.frontmatter.aboutArtifact === taskId);
|
|
14540
|
+
if (children.length > 0) {
|
|
14541
|
+
const avg = children.reduce((sum, c) => sum + getEffectiveProgress(c.frontmatter), 0) / children.length;
|
|
14542
|
+
const progress = Math.round(avg);
|
|
14543
|
+
if (task.frontmatter.progress !== progress) {
|
|
14544
|
+
store.update(taskId, { progress });
|
|
14545
|
+
updated.push(taskId);
|
|
14546
|
+
}
|
|
14547
|
+
}
|
|
14546
14548
|
}
|
|
14547
14549
|
}
|
|
14548
14550
|
const aboutArtifact = task.frontmatter.aboutArtifact;
|
|
@@ -14554,10 +14556,11 @@ function propagateProgressFromTask(store, taskId) {
|
|
|
14554
14556
|
}
|
|
14555
14557
|
return updated;
|
|
14556
14558
|
}
|
|
14557
|
-
function propagateProgressToAction(store, actionId) {
|
|
14559
|
+
function propagateProgressToAction(store, actionId, options) {
|
|
14558
14560
|
const updated = [];
|
|
14559
14561
|
const action = store.get(actionId);
|
|
14560
14562
|
if (!action) return updated;
|
|
14563
|
+
if (options?.skipSelf) return updated;
|
|
14561
14564
|
if (DONE_STATUSES.has(action.frontmatter.status)) {
|
|
14562
14565
|
if (action.frontmatter.progress !== 100) {
|
|
14563
14566
|
store.update(actionId, { progress: 100 });
|
|
@@ -14742,10 +14745,11 @@ function createActionTools(store) {
|
|
|
14742
14745
|
tags: external_exports.array(external_exports.string()).optional().describe("Replace all tags. When provided with sprints, sprint tags are merged into this array."),
|
|
14743
14746
|
sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (replaces existing sprint tags). E.g. ['SP-001']."),
|
|
14744
14747
|
workFocus: external_exports.string().optional().describe("Work focus name (e.g. 'Budget UX'). Replaces existing focus:<value> tag."),
|
|
14745
|
-
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.")
|
|
14748
|
+
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."),
|
|
14749
|
+
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.")
|
|
14746
14750
|
},
|
|
14747
14751
|
async (args) => {
|
|
14748
|
-
const { id, content, sprints, tags, workFocus, progress, owner, assignee, ...updates } = args;
|
|
14752
|
+
const { id, content, sprints, tags, workFocus, progress, progressOverride, owner, assignee, ...updates } = args;
|
|
14749
14753
|
if (owner !== void 0) updates.owner = normalizeOwner(owner);
|
|
14750
14754
|
if (assignee !== void 0) updates.assignee = assignee;
|
|
14751
14755
|
if (tags !== void 0) {
|
|
@@ -14784,13 +14788,17 @@ function createActionTools(store) {
|
|
|
14784
14788
|
}
|
|
14785
14789
|
if (typeof progress === "number") {
|
|
14786
14790
|
updates.progress = Math.max(0, Math.min(100, Math.round(progress)));
|
|
14787
|
-
updates.progressOverride = true;
|
|
14791
|
+
updates.progressOverride = progressOverride ?? true;
|
|
14788
14792
|
} else if (progress === null) {
|
|
14789
14793
|
updates.progressOverride = false;
|
|
14794
|
+
} else if (progressOverride !== void 0) {
|
|
14795
|
+
updates.progressOverride = progressOverride;
|
|
14790
14796
|
}
|
|
14791
14797
|
const doc = store.update(id, updates, content);
|
|
14792
14798
|
if (args.status !== void 0 || typeof progress === "number") {
|
|
14793
|
-
propagateProgressToAction(store, id
|
|
14799
|
+
propagateProgressToAction(store, id, {
|
|
14800
|
+
skipSelf: typeof progress === "number"
|
|
14801
|
+
});
|
|
14794
14802
|
}
|
|
14795
14803
|
return {
|
|
14796
14804
|
content: [
|
|
@@ -23792,10 +23800,11 @@ function createTaskTools(store) {
|
|
|
23792
23800
|
priority: external_exports.enum(["critical", "high", "medium", "low"]).optional().describe("New priority"),
|
|
23793
23801
|
tags: external_exports.array(external_exports.string()).optional().describe("Replace tags (e.g. remove old tags, add new ones)"),
|
|
23794
23802
|
workFocus: external_exports.string().optional().describe("Work focus name (e.g. 'Budget UX'). Replaces existing focus:<value> tag."),
|
|
23795
|
-
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.")
|
|
23803
|
+
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."),
|
|
23804
|
+
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.")
|
|
23796
23805
|
},
|
|
23797
23806
|
async (args) => {
|
|
23798
|
-
const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, workFocus, progress, ...updates } = args;
|
|
23807
|
+
const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, workFocus, progress, progressOverride, ...updates } = args;
|
|
23799
23808
|
const warnings = [];
|
|
23800
23809
|
if (rawLinkedEpic !== void 0) {
|
|
23801
23810
|
const linkedEpics = normalizeLinkedEpics(rawLinkedEpic);
|
|
@@ -23824,13 +23833,17 @@ function createTaskTools(store) {
|
|
|
23824
23833
|
}
|
|
23825
23834
|
if (typeof progress === "number") {
|
|
23826
23835
|
updates.progress = Math.max(0, Math.min(100, Math.round(progress)));
|
|
23827
|
-
updates.progressOverride = true;
|
|
23836
|
+
updates.progressOverride = progressOverride ?? true;
|
|
23828
23837
|
} else if (progress === null) {
|
|
23829
23838
|
updates.progressOverride = false;
|
|
23839
|
+
} else if (progressOverride !== void 0) {
|
|
23840
|
+
updates.progressOverride = progressOverride;
|
|
23830
23841
|
}
|
|
23831
23842
|
const doc = store.update(id, updates, content);
|
|
23832
23843
|
if (args.status !== void 0 || typeof progress === "number") {
|
|
23833
|
-
propagateProgressFromTask(store, id
|
|
23844
|
+
propagateProgressFromTask(store, id, {
|
|
23845
|
+
skipSelf: typeof progress === "number"
|
|
23846
|
+
});
|
|
23834
23847
|
}
|
|
23835
23848
|
const parts = [`Updated task ${doc.frontmatter.id}: ${doc.frontmatter.title}`];
|
|
23836
23849
|
if (warnings.length > 0) {
|
|
@@ -25794,6 +25807,7 @@ function generateProposedActions(issues) {
|
|
|
25794
25807
|
// src/skills/builtin/jira/sprint-progress.ts
|
|
25795
25808
|
import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
|
|
25796
25809
|
var DONE_STATUSES15 = /* @__PURE__ */ new Set(["done", "closed", "resolved", "obsolete", "wont do", "cancelled"]);
|
|
25810
|
+
var PROGRESS_DONE_STATUSES = /* @__PURE__ */ new Set(["done", "closed", "resolved", "obsolete", "wont do"]);
|
|
25797
25811
|
var BATCH_SIZE = 5;
|
|
25798
25812
|
var MAX_LINKED_ISSUES = 50;
|
|
25799
25813
|
var BLOCKED_WEIGHT_RISK_THRESHOLD = 0.3;
|
|
@@ -26720,8 +26734,16 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
|
|
|
26720
26734
|
children.push(childReport);
|
|
26721
26735
|
}
|
|
26722
26736
|
if (children.length > 0) {
|
|
26737
|
+
const childProgressValues = children.map((c) => {
|
|
26738
|
+
const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
|
|
26739
|
+
const lastStatus = findLast(updates, (u) => u.field === "status");
|
|
26740
|
+
if (lastStatus && PROGRESS_DONE_STATUSES.has(String(lastStatus.proposedValue))) return 100;
|
|
26741
|
+
const lastProgress = findLast(updates, (u) => u.field === "progress");
|
|
26742
|
+
if (lastProgress) return lastProgress.proposedValue;
|
|
26743
|
+
return c.marvinProgress;
|
|
26744
|
+
});
|
|
26723
26745
|
const rolledUpProgress = Math.round(
|
|
26724
|
-
|
|
26746
|
+
childProgressValues.reduce((s, p) => s + p, 0) / childProgressValues.length
|
|
26725
26747
|
);
|
|
26726
26748
|
if (rolledUpProgress !== currentProgress) {
|
|
26727
26749
|
proposedUpdates.push({
|
|
@@ -26736,8 +26758,14 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
|
|
|
26736
26758
|
const signals = buildSignals(commentSignals, linkedIssues, statusDrift, proposedMarvinStatus);
|
|
26737
26759
|
const appliedUpdates = [];
|
|
26738
26760
|
if (options.applyUpdates && proposedUpdates.length > 0) {
|
|
26761
|
+
const doneArtifacts = new Set(
|
|
26762
|
+
proposedUpdates.filter((u) => u.field === "status" && PROGRESS_DONE_STATUSES.has(String(u.proposedValue))).map((u) => u.artifactId)
|
|
26763
|
+
);
|
|
26739
26764
|
for (const update of proposedUpdates) {
|
|
26740
26765
|
if (update.field === "review") continue;
|
|
26766
|
+
if (update.field === "progress" && doneArtifacts.has(update.artifactId)) {
|
|
26767
|
+
if (update.proposedValue !== 100) continue;
|
|
26768
|
+
}
|
|
26741
26769
|
try {
|
|
26742
26770
|
const updatePayload = {
|
|
26743
26771
|
[update.field]: update.proposedValue,
|
|
@@ -26747,12 +26775,14 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
|
|
|
26747
26775
|
updatePayload.progressOverride = false;
|
|
26748
26776
|
}
|
|
26749
26777
|
store.update(update.artifactId, updatePayload);
|
|
26750
|
-
|
|
26751
|
-
|
|
26752
|
-
if (updatedDoc
|
|
26753
|
-
|
|
26754
|
-
|
|
26755
|
-
|
|
26778
|
+
if (update.field === "status") {
|
|
26779
|
+
const updatedDoc = store.get(update.artifactId);
|
|
26780
|
+
if (updatedDoc) {
|
|
26781
|
+
if (updatedDoc.frontmatter.type === "task") {
|
|
26782
|
+
propagateProgressFromTask(store, update.artifactId);
|
|
26783
|
+
} else if (updatedDoc.frontmatter.type === "action") {
|
|
26784
|
+
propagateProgressToAction(store, update.artifactId);
|
|
26785
|
+
}
|
|
26756
26786
|
}
|
|
26757
26787
|
}
|
|
26758
26788
|
appliedUpdates.push(update);
|
|
@@ -27057,6 +27087,12 @@ function formatArtifactChild(parts, child, depth) {
|
|
|
27057
27087
|
formatArtifactChild(parts, grandchild, depth + 1);
|
|
27058
27088
|
}
|
|
27059
27089
|
}
|
|
27090
|
+
function findLast(arr, predicate) {
|
|
27091
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
27092
|
+
if (predicate(arr[i])) return arr[i];
|
|
27093
|
+
}
|
|
27094
|
+
return void 0;
|
|
27095
|
+
}
|
|
27060
27096
|
|
|
27061
27097
|
// src/skills/builtin/jira/tools.ts
|
|
27062
27098
|
var JIRA_TYPE = "jira-issue";
|
|
@@ -33554,7 +33590,7 @@ function createProgram() {
|
|
|
33554
33590
|
const program = new Command();
|
|
33555
33591
|
program.name("marvin").description(
|
|
33556
33592
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
33557
|
-
).version("0.5.
|
|
33593
|
+
).version("0.5.22");
|
|
33558
33594
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
33559
33595
|
await initCommand();
|
|
33560
33596
|
});
|