mrvn-cli 0.5.21 → 0.5.23
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 +64 -14
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +63 -13
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +64 -14
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25807,6 +25807,7 @@ function generateProposedActions(issues) {
|
|
|
25807
25807
|
// src/skills/builtin/jira/sprint-progress.ts
|
|
25808
25808
|
import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
|
|
25809
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"]);
|
|
25810
25811
|
var BATCH_SIZE = 5;
|
|
25811
25812
|
var MAX_LINKED_ISSUES = 50;
|
|
25812
25813
|
var BLOCKED_WEIGHT_RISK_THRESHOLD = 0.3;
|
|
@@ -26696,11 +26697,12 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
|
|
|
26696
26697
|
}
|
|
26697
26698
|
}
|
|
26698
26699
|
const primaryHasComments = jiraKey ? (jiraIssues.get(jiraKey)?.comments.length ?? 0) > 0 : false;
|
|
26700
|
+
let commentAnalysisProgress = null;
|
|
26699
26701
|
if (depth < MAX_LLM_DEPTH && jiraKey && primaryHasComments) {
|
|
26700
26702
|
const estimatedChars = estimateCommentTextSize(jiraIssues, linkedJiraIssues, linkedIssueSignals);
|
|
26701
26703
|
if (estimatedChars <= MAX_LLM_COMMENT_CHARS) {
|
|
26702
26704
|
try {
|
|
26703
|
-
const
|
|
26705
|
+
const analysis = await analyzeSingleArtifactComments(
|
|
26704
26706
|
fm.id,
|
|
26705
26707
|
fm.title,
|
|
26706
26708
|
jiraKey,
|
|
@@ -26709,7 +26711,20 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
|
|
|
26709
26711
|
linkedJiraIssues,
|
|
26710
26712
|
linkedIssueSignals
|
|
26711
26713
|
);
|
|
26712
|
-
commentSummary = summary;
|
|
26714
|
+
commentSummary = analysis.summary;
|
|
26715
|
+
commentAnalysisProgress = analysis.progressEstimate;
|
|
26716
|
+
if (commentAnalysisProgress !== null) {
|
|
26717
|
+
const hasExplicitProgress = "progress" in fm && typeof fm.progress === "number";
|
|
26718
|
+
if (!hasExplicitProgress && !fm.progressOverride && commentAnalysisProgress !== currentProgress) {
|
|
26719
|
+
proposedUpdates.push({
|
|
26720
|
+
artifactId: fm.id,
|
|
26721
|
+
field: "progress",
|
|
26722
|
+
currentValue: currentProgress,
|
|
26723
|
+
proposedValue: commentAnalysisProgress,
|
|
26724
|
+
reason: `Comment analysis estimates ${commentAnalysisProgress}% progress`
|
|
26725
|
+
});
|
|
26726
|
+
}
|
|
26727
|
+
}
|
|
26713
26728
|
} catch (err) {
|
|
26714
26729
|
errors.push(`Comment analysis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26715
26730
|
}
|
|
@@ -26733,8 +26748,16 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
|
|
|
26733
26748
|
children.push(childReport);
|
|
26734
26749
|
}
|
|
26735
26750
|
if (children.length > 0) {
|
|
26751
|
+
const childProgressValues = children.map((c) => {
|
|
26752
|
+
const updates = c.appliedUpdates.length > 0 ? c.appliedUpdates : c.proposedUpdates;
|
|
26753
|
+
const lastStatus = findLast(updates, (u) => u.field === "status");
|
|
26754
|
+
if (lastStatus && PROGRESS_DONE_STATUSES.has(String(lastStatus.proposedValue))) return 100;
|
|
26755
|
+
const lastProgress = findLast(updates, (u) => u.field === "progress");
|
|
26756
|
+
if (lastProgress) return lastProgress.proposedValue;
|
|
26757
|
+
return c.marvinProgress;
|
|
26758
|
+
});
|
|
26736
26759
|
const rolledUpProgress = Math.round(
|
|
26737
|
-
|
|
26760
|
+
childProgressValues.reduce((s, p) => s + p, 0) / childProgressValues.length
|
|
26738
26761
|
);
|
|
26739
26762
|
if (rolledUpProgress !== currentProgress) {
|
|
26740
26763
|
proposedUpdates.push({
|
|
@@ -26750,7 +26773,7 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
|
|
|
26750
26773
|
const appliedUpdates = [];
|
|
26751
26774
|
if (options.applyUpdates && proposedUpdates.length > 0) {
|
|
26752
26775
|
const doneArtifacts = new Set(
|
|
26753
|
-
proposedUpdates.filter((u) => u.field === "status" &&
|
|
26776
|
+
proposedUpdates.filter((u) => u.field === "status" && PROGRESS_DONE_STATUSES.has(String(u.proposedValue))).map((u) => u.artifactId)
|
|
26754
26777
|
);
|
|
26755
26778
|
for (const update of proposedUpdates) {
|
|
26756
26779
|
if (update.field === "review") continue;
|
|
@@ -26799,6 +26822,7 @@ async function _assessArtifactRecursive(store, client, host, options, visited, d
|
|
|
26799
26822
|
progressDrift,
|
|
26800
26823
|
commentSignals,
|
|
26801
26824
|
commentSummary,
|
|
26825
|
+
commentAnalysisProgress,
|
|
26802
26826
|
linkedIssues,
|
|
26803
26827
|
linkedIssueSignals,
|
|
26804
26828
|
children,
|
|
@@ -26891,13 +26915,15 @@ function estimateCommentTextSize(jiraIssues, linkedJiraIssues, linkedIssueSignal
|
|
|
26891
26915
|
}
|
|
26892
26916
|
var SINGLE_ARTIFACT_COMMENT_PROMPT = `You are a delivery management assistant analyzing Jira comments for a single work item.
|
|
26893
26917
|
|
|
26894
|
-
|
|
26895
|
-
-
|
|
26896
|
-
-
|
|
26897
|
-
- Any decisions, handoffs, or scheduling mentioned
|
|
26898
|
-
- Relevant context from linked issue comments (if provided)
|
|
26918
|
+
Analyze the comments and produce:
|
|
26919
|
+
1. A 2-3 sentence progress summary covering: what work has been completed, what is pending or blocked, any decisions/handoffs/scheduling mentioned, and relevant context from linked issue comments (if provided).
|
|
26920
|
+
2. A progress estimate (0-100%) based on evidence in the comments \u2014 e.g., if comments indicate all items have been triaged into tasks, or implementation is complete pending review, estimate accordingly. If you cannot determine progress from the comments, set progressEstimate to null.
|
|
26899
26921
|
|
|
26900
|
-
Return
|
|
26922
|
+
Return a JSON object with this exact structure:
|
|
26923
|
+
{"summary": "your 2-3 sentence summary", "progressEstimate": 75}
|
|
26924
|
+
|
|
26925
|
+
Use null for progressEstimate if the comments don't provide enough evidence to estimate.
|
|
26926
|
+
IMPORTANT: Only return the JSON object, no other text.`;
|
|
26901
26927
|
async function analyzeSingleArtifactComments(artifactId, title, jiraKey, jiraStatus, jiraIssues, linkedJiraIssues, linkedIssueSignals) {
|
|
26902
26928
|
const promptParts = [];
|
|
26903
26929
|
const primaryData = jiraIssues.get(jiraKey);
|
|
@@ -26920,7 +26946,7 @@ ${commentTexts}`);
|
|
|
26920
26946
|
promptParts.push(`### Linked: ${signal.sourceKey} (${signal.linkType})
|
|
26921
26947
|
${commentTexts}`);
|
|
26922
26948
|
}
|
|
26923
|
-
if (promptParts.length === 0) return null;
|
|
26949
|
+
if (promptParts.length === 0) return { summary: null, progressEstimate: null };
|
|
26924
26950
|
const prompt = promptParts.join("\n\n");
|
|
26925
26951
|
const result = query3({
|
|
26926
26952
|
prompt,
|
|
@@ -26937,11 +26963,25 @@ ${commentTexts}`);
|
|
|
26937
26963
|
(b) => b.type === "text"
|
|
26938
26964
|
);
|
|
26939
26965
|
if (textBlock) {
|
|
26940
|
-
return textBlock.text.trim();
|
|
26966
|
+
return parseCommentAnalysis(textBlock.text.trim());
|
|
26941
26967
|
}
|
|
26942
26968
|
}
|
|
26943
26969
|
}
|
|
26944
|
-
return null;
|
|
26970
|
+
return { summary: null, progressEstimate: null };
|
|
26971
|
+
}
|
|
26972
|
+
function parseCommentAnalysis(text) {
|
|
26973
|
+
const parsed = parseLlmJson(text);
|
|
26974
|
+
if (parsed && typeof parsed.summary === "string") {
|
|
26975
|
+
const progressEstimate2 = typeof parsed.progressEstimate === "number" && parsed.progressEstimate >= 0 && parsed.progressEstimate <= 100 ? Math.round(parsed.progressEstimate) : null;
|
|
26976
|
+
return { summary: parsed.summary, progressEstimate: progressEstimate2 };
|
|
26977
|
+
}
|
|
26978
|
+
let progressEstimate = null;
|
|
26979
|
+
const pctMatch = text.match(/(\d{1,3})%/);
|
|
26980
|
+
if (pctMatch) {
|
|
26981
|
+
const pct = parseInt(pctMatch[1], 10);
|
|
26982
|
+
if (pct >= 0 && pct <= 100) progressEstimate = pct;
|
|
26983
|
+
}
|
|
26984
|
+
return { summary: text, progressEstimate };
|
|
26945
26985
|
}
|
|
26946
26986
|
function emptyArtifactReport(artifactId, errors) {
|
|
26947
26987
|
return {
|
|
@@ -26961,6 +27001,7 @@ function emptyArtifactReport(artifactId, errors) {
|
|
|
26961
27001
|
progressDrift: false,
|
|
26962
27002
|
commentSignals: [],
|
|
26963
27003
|
commentSummary: null,
|
|
27004
|
+
commentAnalysisProgress: null,
|
|
26964
27005
|
linkedIssues: [],
|
|
26965
27006
|
linkedIssueSignals: [],
|
|
26966
27007
|
children: [],
|
|
@@ -26998,6 +27039,9 @@ function formatArtifactReport(report) {
|
|
|
26998
27039
|
if (report.commentSummary) {
|
|
26999
27040
|
parts.push(`## Comments`);
|
|
27000
27041
|
parts.push(report.commentSummary);
|
|
27042
|
+
if (report.commentAnalysisProgress !== null) {
|
|
27043
|
+
parts.push(` \u{1F4CA} Comment-derived progress estimate: ${report.commentAnalysisProgress}%`);
|
|
27044
|
+
}
|
|
27001
27045
|
parts.push("");
|
|
27002
27046
|
}
|
|
27003
27047
|
if (report.children.length > 0) {
|
|
@@ -27078,6 +27122,12 @@ function formatArtifactChild(parts, child, depth) {
|
|
|
27078
27122
|
formatArtifactChild(parts, grandchild, depth + 1);
|
|
27079
27123
|
}
|
|
27080
27124
|
}
|
|
27125
|
+
function findLast(arr, predicate) {
|
|
27126
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
27127
|
+
if (predicate(arr[i])) return arr[i];
|
|
27128
|
+
}
|
|
27129
|
+
return void 0;
|
|
27130
|
+
}
|
|
27081
27131
|
|
|
27082
27132
|
// src/skills/builtin/jira/tools.ts
|
|
27083
27133
|
var JIRA_TYPE = "jira-issue";
|
|
@@ -33575,7 +33625,7 @@ function createProgram() {
|
|
|
33575
33625
|
const program = new Command();
|
|
33576
33626
|
program.name("marvin").description(
|
|
33577
33627
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
33578
|
-
).version("0.5.
|
|
33628
|
+
).version("0.5.23");
|
|
33579
33629
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
33580
33630
|
await initCommand();
|
|
33581
33631
|
});
|