mrvn-cli 0.5.14 → 0.5.16
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.d.ts +5 -1
- package/dist/index.js +168 -36
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +163 -33
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +168 -36
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -16,8 +16,12 @@ interface MarvinUserConfig {
|
|
|
16
16
|
interface GitConfig {
|
|
17
17
|
remote?: string;
|
|
18
18
|
}
|
|
19
|
+
interface ConditionalJiraStatusEntry {
|
|
20
|
+
default: string[];
|
|
21
|
+
inSprint?: string[];
|
|
22
|
+
}
|
|
19
23
|
interface JiraStatusMap {
|
|
20
|
-
[marvinStatus: string]: string[];
|
|
24
|
+
[marvinStatus: string]: string[] | ConditionalJiraStatusEntry;
|
|
21
25
|
}
|
|
22
26
|
interface JiraProjectConfig {
|
|
23
27
|
projectKey?: string;
|
package/dist/index.js
CHANGED
|
@@ -25092,24 +25092,53 @@ var DEFAULT_TASK_STATUS_MAP = {
|
|
|
25092
25092
|
blocked: ["Blocked"],
|
|
25093
25093
|
backlog: ["To Do", "Open", "Backlog", "New"]
|
|
25094
25094
|
};
|
|
25095
|
-
function
|
|
25095
|
+
function isConditionalEntry(value) {
|
|
25096
|
+
return !Array.isArray(value) && typeof value === "object" && "default" in value;
|
|
25097
|
+
}
|
|
25098
|
+
function buildStatusLookup(configMap, defaults, inSprint = false) {
|
|
25096
25099
|
const map2 = configMap ?? defaults;
|
|
25097
25100
|
const lookup = /* @__PURE__ */ new Map();
|
|
25098
|
-
for (const [marvinStatus,
|
|
25099
|
-
|
|
25101
|
+
for (const [marvinStatus, value] of Object.entries(map2)) {
|
|
25102
|
+
const statuses = isConditionalEntry(value) ? value.default : value;
|
|
25103
|
+
for (const js of statuses) {
|
|
25100
25104
|
lookup.set(js.toLowerCase(), marvinStatus);
|
|
25101
25105
|
}
|
|
25102
25106
|
}
|
|
25107
|
+
if (inSprint) {
|
|
25108
|
+
for (const [marvinStatus, value] of Object.entries(map2)) {
|
|
25109
|
+
if (isConditionalEntry(value) && value.inSprint) {
|
|
25110
|
+
for (const js of value.inSprint) {
|
|
25111
|
+
lookup.set(js.toLowerCase(), marvinStatus);
|
|
25112
|
+
}
|
|
25113
|
+
}
|
|
25114
|
+
}
|
|
25115
|
+
}
|
|
25103
25116
|
return lookup;
|
|
25104
25117
|
}
|
|
25105
|
-
function mapJiraStatusForAction(status, configMap) {
|
|
25106
|
-
const lookup = buildStatusLookup(configMap, DEFAULT_ACTION_STATUS_MAP);
|
|
25118
|
+
function mapJiraStatusForAction(status, configMap, inSprint) {
|
|
25119
|
+
const lookup = buildStatusLookup(configMap, DEFAULT_ACTION_STATUS_MAP, inSprint ?? false);
|
|
25107
25120
|
return lookup.get(status.toLowerCase()) ?? "open";
|
|
25108
25121
|
}
|
|
25109
|
-
function mapJiraStatusForTask(status, configMap) {
|
|
25110
|
-
const lookup = buildStatusLookup(configMap, DEFAULT_TASK_STATUS_MAP);
|
|
25122
|
+
function mapJiraStatusForTask(status, configMap, inSprint) {
|
|
25123
|
+
const lookup = buildStatusLookup(configMap, DEFAULT_TASK_STATUS_MAP, inSprint ?? false);
|
|
25111
25124
|
return lookup.get(status.toLowerCase()) ?? "backlog";
|
|
25112
25125
|
}
|
|
25126
|
+
function isInActiveSprint(store, tags) {
|
|
25127
|
+
if (!tags) return false;
|
|
25128
|
+
const sprintTags = tags.filter((t) => t.startsWith("sprint:"));
|
|
25129
|
+
if (sprintTags.length === 0) return false;
|
|
25130
|
+
for (const tag of sprintTags) {
|
|
25131
|
+
const sprintId = tag.slice(7);
|
|
25132
|
+
const sprintDoc = store.get(sprintId);
|
|
25133
|
+
if (sprintDoc) {
|
|
25134
|
+
const status = sprintDoc.frontmatter.status;
|
|
25135
|
+
if (status === "active" || status === "completed") {
|
|
25136
|
+
return true;
|
|
25137
|
+
}
|
|
25138
|
+
}
|
|
25139
|
+
}
|
|
25140
|
+
return false;
|
|
25141
|
+
}
|
|
25113
25142
|
function extractJiraKeyFromTags(tags) {
|
|
25114
25143
|
if (!tags) return void 0;
|
|
25115
25144
|
const tag = tags.find((t) => /^jira:[A-Z]+-\d+$/i.test(t));
|
|
@@ -25151,7 +25180,8 @@ async function fetchJiraStatus(store, client, host, artifactId, statusMap) {
|
|
|
25151
25180
|
const artifactType = doc.frontmatter.type;
|
|
25152
25181
|
try {
|
|
25153
25182
|
const issue2 = await client.getIssueWithLinks(jiraKey);
|
|
25154
|
-
const
|
|
25183
|
+
const inSprint = isInActiveSprint(store, doc.frontmatter.tags);
|
|
25184
|
+
const proposedStatus = artifactType === "task" ? mapJiraStatusForTask(issue2.fields.status.name, statusMap?.task, inSprint) : mapJiraStatusForAction(issue2.fields.status.name, statusMap?.action, inSprint);
|
|
25155
25185
|
const currentStatus = doc.frontmatter.status;
|
|
25156
25186
|
const linkedIssues = [];
|
|
25157
25187
|
if (issue2.fields.subtasks) {
|
|
@@ -25503,7 +25533,7 @@ async function fetchJiraDaily(store, client, host, projectKey, dateRange, status
|
|
|
25503
25533
|
const batch = issues.slice(i, i + BATCH_SIZE2);
|
|
25504
25534
|
const results = await Promise.allSettled(
|
|
25505
25535
|
batch.map(
|
|
25506
|
-
(issue2) => processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap)
|
|
25536
|
+
(issue2) => processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap, store)
|
|
25507
25537
|
)
|
|
25508
25538
|
);
|
|
25509
25539
|
for (let j = 0; j < results.length; j++) {
|
|
@@ -25520,7 +25550,7 @@ async function fetchJiraDaily(store, client, host, projectKey, dateRange, status
|
|
|
25520
25550
|
summary.proposedActions = generateProposedActions(summary.issues);
|
|
25521
25551
|
return summary;
|
|
25522
25552
|
}
|
|
25523
|
-
async function processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap) {
|
|
25553
|
+
async function processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap, store) {
|
|
25524
25554
|
const [changelogResult, commentsResult, remoteLinksResult, issueWithLinks] = await Promise.all([
|
|
25525
25555
|
client.getChangelog(issue2.key).catch(() => []),
|
|
25526
25556
|
client.getComments(issue2.key).catch(() => []),
|
|
@@ -25608,7 +25638,8 @@ async function processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts,
|
|
|
25608
25638
|
if (artifactType === "action" || artifactType === "task") {
|
|
25609
25639
|
const jiraStatus = issue2.fields.status?.name;
|
|
25610
25640
|
if (jiraStatus) {
|
|
25611
|
-
|
|
25641
|
+
const inSprint = store ? isInActiveSprint(store, fm.tags) : false;
|
|
25642
|
+
proposedStatus = artifactType === "task" ? mapJiraStatusForTask(jiraStatus, statusMap?.task, inSprint) : mapJiraStatusForAction(jiraStatus, statusMap?.action, inSprint);
|
|
25612
25643
|
}
|
|
25613
25644
|
}
|
|
25614
25645
|
marvinArtifacts.push({
|
|
@@ -25728,6 +25759,61 @@ function generateProposedActions(issues) {
|
|
|
25728
25759
|
import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
|
|
25729
25760
|
var DONE_STATUSES16 = /* @__PURE__ */ new Set(["done", "closed", "resolved", "obsolete", "wont do", "cancelled"]);
|
|
25730
25761
|
var BATCH_SIZE = 5;
|
|
25762
|
+
var BLOCKED_WEIGHT_RISK_THRESHOLD = 0.3;
|
|
25763
|
+
var COMPLEXITY_WEIGHTS = {
|
|
25764
|
+
trivial: 1,
|
|
25765
|
+
simple: 2,
|
|
25766
|
+
moderate: 3,
|
|
25767
|
+
complex: 5,
|
|
25768
|
+
"very-complex": 8
|
|
25769
|
+
};
|
|
25770
|
+
var DEFAULT_WEIGHT = 3;
|
|
25771
|
+
var STATUS_PROGRESS_DEFAULTS = {
|
|
25772
|
+
done: 100,
|
|
25773
|
+
closed: 100,
|
|
25774
|
+
resolved: 100,
|
|
25775
|
+
obsolete: 100,
|
|
25776
|
+
"wont do": 100,
|
|
25777
|
+
cancelled: 100,
|
|
25778
|
+
review: 80,
|
|
25779
|
+
"in-progress": 40,
|
|
25780
|
+
ready: 5,
|
|
25781
|
+
backlog: 0,
|
|
25782
|
+
open: 0
|
|
25783
|
+
};
|
|
25784
|
+
var BLOCKED_DEFAULT_PROGRESS = 10;
|
|
25785
|
+
function resolveWeight(complexity) {
|
|
25786
|
+
if (complexity && complexity in COMPLEXITY_WEIGHTS) {
|
|
25787
|
+
return { weight: COMPLEXITY_WEIGHTS[complexity], weightSource: "complexity" };
|
|
25788
|
+
}
|
|
25789
|
+
return { weight: DEFAULT_WEIGHT, weightSource: "default" };
|
|
25790
|
+
}
|
|
25791
|
+
function resolveProgress(frontmatter, commentAnalysisProgress) {
|
|
25792
|
+
const hasExplicitProgress = "progress" in frontmatter && typeof frontmatter.progress === "number";
|
|
25793
|
+
if (hasExplicitProgress) {
|
|
25794
|
+
return { progress: Math.max(0, Math.min(100, Math.round(frontmatter.progress))), progressSource: "explicit" };
|
|
25795
|
+
}
|
|
25796
|
+
if (commentAnalysisProgress !== null) {
|
|
25797
|
+
return { progress: Math.max(0, Math.min(100, Math.round(commentAnalysisProgress))), progressSource: "comment-analysis" };
|
|
25798
|
+
}
|
|
25799
|
+
const status = frontmatter.status;
|
|
25800
|
+
if (status === "blocked") {
|
|
25801
|
+
return { progress: BLOCKED_DEFAULT_PROGRESS, progressSource: "status-default" };
|
|
25802
|
+
}
|
|
25803
|
+
const defaultProgress = STATUS_PROGRESS_DEFAULTS[status] ?? 0;
|
|
25804
|
+
return { progress: defaultProgress, progressSource: "status-default" };
|
|
25805
|
+
}
|
|
25806
|
+
function computeWeightedProgress(items) {
|
|
25807
|
+
if (items.length === 0) return 0;
|
|
25808
|
+
let totalWeight = 0;
|
|
25809
|
+
let weightedSum = 0;
|
|
25810
|
+
for (const item of items) {
|
|
25811
|
+
totalWeight += item.weight;
|
|
25812
|
+
weightedSum += item.weight * item.progress;
|
|
25813
|
+
}
|
|
25814
|
+
if (totalWeight === 0) return 0;
|
|
25815
|
+
return Math.round(weightedSum / totalWeight);
|
|
25816
|
+
}
|
|
25731
25817
|
async function assessSprintProgress(store, client, host, options = {}) {
|
|
25732
25818
|
const errors = [];
|
|
25733
25819
|
const sprintData = collectSprintSummaryData(store, options.sprintId);
|
|
@@ -25805,7 +25891,8 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
25805
25891
|
const commentSignals = [];
|
|
25806
25892
|
if (jiraData) {
|
|
25807
25893
|
jiraStatus = jiraData.issue.fields.status.name;
|
|
25808
|
-
|
|
25894
|
+
const inSprint = isInActiveSprint(store, fm.tags);
|
|
25895
|
+
proposedMarvinStatus = fm.type === "task" ? mapJiraStatusForTask(jiraStatus, options.statusMap?.task, inSprint) : mapJiraStatusForAction(jiraStatus, options.statusMap?.action, inSprint);
|
|
25809
25896
|
const subtasks = jiraData.issue.fields.subtasks ?? [];
|
|
25810
25897
|
if (subtasks.length > 0) {
|
|
25811
25898
|
jiraSubtaskProgress = computeSubtaskProgress(subtasks);
|
|
@@ -25839,12 +25926,18 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
25839
25926
|
}
|
|
25840
25927
|
const tags = fm.tags ?? [];
|
|
25841
25928
|
const focusTag = tags.find((t) => t.startsWith("focus:"));
|
|
25929
|
+
const { weight, weightSource } = resolveWeight(fm.complexity);
|
|
25930
|
+
const { progress: resolvedProgress, progressSource } = resolveProgress(fm, null);
|
|
25842
25931
|
const report = {
|
|
25843
25932
|
id: fm.id,
|
|
25844
25933
|
title: fm.title,
|
|
25845
25934
|
type: fm.type,
|
|
25846
25935
|
marvinStatus: fm.status,
|
|
25847
25936
|
marvinProgress: currentProgress,
|
|
25937
|
+
progress: resolvedProgress,
|
|
25938
|
+
progressSource,
|
|
25939
|
+
weight,
|
|
25940
|
+
weightSource,
|
|
25848
25941
|
jiraKey,
|
|
25849
25942
|
jiraStatus,
|
|
25850
25943
|
jiraSubtaskProgress,
|
|
@@ -25877,32 +25970,44 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
25877
25970
|
for (const child of children) childIds.add(child.id);
|
|
25878
25971
|
}
|
|
25879
25972
|
const rootReports = itemReports.filter((r) => !childIds.has(r.id));
|
|
25973
|
+
for (const report of rootReports) {
|
|
25974
|
+
if (report.children.length > 0) {
|
|
25975
|
+
const doc = store.get(report.id);
|
|
25976
|
+
const hasExplicitOverride = doc?.frontmatter.progressOverride;
|
|
25977
|
+
if (!hasExplicitOverride) {
|
|
25978
|
+
report.progress = computeWeightedProgress(report.children);
|
|
25979
|
+
report.progressSource = "status-default";
|
|
25980
|
+
}
|
|
25981
|
+
}
|
|
25982
|
+
}
|
|
25880
25983
|
const focusAreaMap = /* @__PURE__ */ new Map();
|
|
25881
25984
|
for (const report of rootReports) {
|
|
25882
|
-
|
|
25883
|
-
if (!focusAreaMap.has(
|
|
25884
|
-
focusAreaMap.get(
|
|
25985
|
+
if (!report.focusArea) continue;
|
|
25986
|
+
if (!focusAreaMap.has(report.focusArea)) focusAreaMap.set(report.focusArea, []);
|
|
25987
|
+
focusAreaMap.get(report.focusArea).push(report);
|
|
25885
25988
|
}
|
|
25886
25989
|
const focusAreas = [];
|
|
25887
25990
|
for (const [name, items] of focusAreaMap) {
|
|
25888
25991
|
const allFlatItems = items.flatMap((i) => [i, ...i.children]);
|
|
25889
25992
|
const doneCount = allFlatItems.filter((i) => DONE_STATUSES16.has(i.marvinStatus)).length;
|
|
25890
25993
|
const blockedCount = allFlatItems.filter((i) => i.marvinStatus === "blocked").length;
|
|
25891
|
-
const
|
|
25994
|
+
const progress = computeWeightedProgress(items);
|
|
25995
|
+
const totalWeight = items.reduce((s, i) => s + i.weight, 0);
|
|
25996
|
+
const blockedWeight = items.filter((i) => i.marvinStatus === "blocked").reduce((s, i) => s + i.weight, 0);
|
|
25997
|
+
const blockedWeightPct = totalWeight > 0 ? Math.round(blockedWeight / totalWeight * 100) : 0;
|
|
25998
|
+
const riskWarning = blockedWeightPct > BLOCKED_WEIGHT_RISK_THRESHOLD * 100 ? `${blockedWeightPct}% of scope is blocked` : null;
|
|
25892
25999
|
focusAreas.push({
|
|
25893
26000
|
name,
|
|
25894
|
-
|
|
25895
|
-
|
|
26001
|
+
progress,
|
|
26002
|
+
taskCount: allFlatItems.length,
|
|
25896
26003
|
doneCount,
|
|
25897
26004
|
blockedCount,
|
|
25898
|
-
|
|
26005
|
+
blockedWeightPct,
|
|
26006
|
+
riskWarning,
|
|
26007
|
+
items
|
|
25899
26008
|
});
|
|
25900
26009
|
}
|
|
25901
|
-
focusAreas.sort((a, b) =>
|
|
25902
|
-
if (a.name === "Uncategorized") return 1;
|
|
25903
|
-
if (b.name === "Uncategorized") return -1;
|
|
25904
|
-
return a.name.localeCompare(b.name);
|
|
25905
|
-
});
|
|
26010
|
+
focusAreas.sort((a, b) => a.name.localeCompare(b.name));
|
|
25906
26011
|
const driftItems = itemReports.filter((r) => r.statusDrift || r.progressDrift);
|
|
25907
26012
|
const blockers = itemReports.filter(
|
|
25908
26013
|
(r) => r.marvinStatus === "blocked" || r.commentSignals.some((s) => s.type === "blocker")
|
|
@@ -25918,7 +26023,19 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
25918
26023
|
);
|
|
25919
26024
|
for (const [artifactId, summary] of summaries) {
|
|
25920
26025
|
const report = itemReports.find((r) => r.id === artifactId);
|
|
25921
|
-
if (report)
|
|
26026
|
+
if (report) {
|
|
26027
|
+
report.commentSummary = summary;
|
|
26028
|
+
if (report.progressSource === "status-default") {
|
|
26029
|
+
const pctMatch = summary.match(/(\d{1,3})%/);
|
|
26030
|
+
if (pctMatch) {
|
|
26031
|
+
const pct = parseInt(pctMatch[1], 10);
|
|
26032
|
+
if (pct >= 0 && pct <= 100) {
|
|
26033
|
+
report.progress = pct;
|
|
26034
|
+
report.progressSource = "comment-analysis";
|
|
26035
|
+
}
|
|
26036
|
+
}
|
|
26037
|
+
}
|
|
26038
|
+
}
|
|
25922
26039
|
}
|
|
25923
26040
|
} catch (err) {
|
|
25924
26041
|
errors.push(`Comment analysis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -25960,7 +26077,7 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
25960
26077
|
totalDays: sprintData.timeline.totalDays,
|
|
25961
26078
|
percentComplete: sprintData.timeline.percentComplete
|
|
25962
26079
|
},
|
|
25963
|
-
overallProgress: sprintData.workItems.completionPct,
|
|
26080
|
+
overallProgress: rootReports.length > 0 ? computeWeightedProgress(rootReports) : sprintData.workItems.completionPct,
|
|
25964
26081
|
itemReports: rootReports,
|
|
25965
26082
|
focusAreas,
|
|
25966
26083
|
driftItems,
|
|
@@ -26057,9 +26174,12 @@ function formatProgressReport(report) {
|
|
|
26057
26174
|
parts.push(`## Focus Areas`);
|
|
26058
26175
|
parts.push("");
|
|
26059
26176
|
for (const area of report.focusAreas) {
|
|
26060
|
-
const bar = progressBar6(area.
|
|
26061
|
-
parts.push(`### ${area.name} ${bar} ${area.
|
|
26062
|
-
parts.push(`${area.doneCount}/${area.
|
|
26177
|
+
const bar = progressBar6(area.progress);
|
|
26178
|
+
parts.push(`### ${area.name} ${bar} ${area.progress}%`);
|
|
26179
|
+
parts.push(`${area.doneCount}/${area.taskCount} done${area.blockedCount > 0 ? ` | ${area.blockedCount} blocked` : ""}`);
|
|
26180
|
+
if (area.riskWarning) {
|
|
26181
|
+
parts.push(` \u26A0 ${area.riskWarning}`);
|
|
26182
|
+
}
|
|
26063
26183
|
parts.push("");
|
|
26064
26184
|
for (const item of area.items) {
|
|
26065
26185
|
formatItemLine(parts, item, 0);
|
|
@@ -26123,8 +26243,10 @@ function formatItemLine(parts, item, depth) {
|
|
|
26123
26243
|
const statusIcon = DONE_STATUSES16.has(item.marvinStatus) ? "\u2713" : item.marvinStatus === "blocked" ? "\u{1F6AB}" : item.marvinStatus === "in-progress" ? "\u25B6" : "\u25CB";
|
|
26124
26244
|
const jiraLabel = item.jiraKey ? ` [${item.jiraKey}: ${item.jiraStatus}]` : "";
|
|
26125
26245
|
const driftFlag = item.statusDrift ? " \u26A0drift" : "";
|
|
26126
|
-
const progressLabel =
|
|
26127
|
-
|
|
26246
|
+
const progressLabel = ` ${item.progress}%`;
|
|
26247
|
+
const weightLabel = `w${item.weight}`;
|
|
26248
|
+
const sourceLabel = item.progressSource === "explicit" ? "" : item.progressSource === "comment-analysis" ? " (llm)" : " (est)";
|
|
26249
|
+
parts.push(`${indent}${statusIcon} ${item.id} \u2014 ${item.title} [${item.marvinStatus}]${progressLabel}${sourceLabel} (${weightLabel})${jiraLabel}${driftFlag}`);
|
|
26128
26250
|
if (item.commentSummary) {
|
|
26129
26251
|
parts.push(`${indent} \u{1F4AC} ${item.commentSummary}`);
|
|
26130
26252
|
}
|
|
@@ -26809,12 +26931,20 @@ function createJiraTools(store, projectConfig) {
|
|
|
26809
26931
|
const actionMap = statusMap?.action ?? DEFAULT_ACTION_STATUS_MAP;
|
|
26810
26932
|
const taskMap = statusMap?.task ?? DEFAULT_TASK_STATUS_MAP;
|
|
26811
26933
|
const actionLookup = /* @__PURE__ */ new Map();
|
|
26812
|
-
for (const [marvin,
|
|
26934
|
+
for (const [marvin, value] of Object.entries(actionMap)) {
|
|
26935
|
+
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
26813
26936
|
for (const js of jiraStatuses) actionLookup.set(js.toLowerCase(), marvin);
|
|
26937
|
+
if (!Array.isArray(value) && value.inSprint) {
|
|
26938
|
+
for (const js of value.inSprint) actionLookup.set(js.toLowerCase(), `${marvin} (inSprint)`);
|
|
26939
|
+
}
|
|
26814
26940
|
}
|
|
26815
26941
|
const taskLookup = /* @__PURE__ */ new Map();
|
|
26816
|
-
for (const [marvin,
|
|
26942
|
+
for (const [marvin, value] of Object.entries(taskMap)) {
|
|
26943
|
+
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
26817
26944
|
for (const js of jiraStatuses) taskLookup.set(js.toLowerCase(), marvin);
|
|
26945
|
+
if (!Array.isArray(value) && value.inSprint) {
|
|
26946
|
+
for (const js of value.inSprint) taskLookup.set(js.toLowerCase(), `${marvin} (inSprint)`);
|
|
26947
|
+
}
|
|
26818
26948
|
}
|
|
26819
26949
|
const parts = [
|
|
26820
26950
|
`Found ${statusCounts.size} distinct statuses in ${resolvedProjectKey} (scanned ${data.issues.length} of ${data.total} issues):`,
|
|
@@ -32385,11 +32515,13 @@ async function jiraStatusesCommand(projectKey) {
|
|
|
32385
32515
|
statusCounts.set(s, (statusCounts.get(s) ?? 0) + 1);
|
|
32386
32516
|
}
|
|
32387
32517
|
const actionLookup = /* @__PURE__ */ new Map();
|
|
32388
|
-
for (const [marvin,
|
|
32518
|
+
for (const [marvin, value] of Object.entries(actionMap)) {
|
|
32519
|
+
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
32389
32520
|
for (const js of jiraStatuses) actionLookup.set(js.toLowerCase(), marvin);
|
|
32390
32521
|
}
|
|
32391
32522
|
const taskLookup = /* @__PURE__ */ new Map();
|
|
32392
|
-
for (const [marvin,
|
|
32523
|
+
for (const [marvin, value] of Object.entries(taskMap)) {
|
|
32524
|
+
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
32393
32525
|
for (const js of jiraStatuses) taskLookup.set(js.toLowerCase(), marvin);
|
|
32394
32526
|
}
|
|
32395
32527
|
console.log(
|
|
@@ -32572,7 +32704,7 @@ function createProgram() {
|
|
|
32572
32704
|
const program = new Command();
|
|
32573
32705
|
program.name("marvin").description(
|
|
32574
32706
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
32575
|
-
).version("0.5.
|
|
32707
|
+
).version("0.5.16");
|
|
32576
32708
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
32577
32709
|
await initCommand();
|
|
32578
32710
|
});
|